From 84dac5211b3307a58bdbca156b019e8dce1aa125 Mon Sep 17 00:00:00 2001 From: wh Date: Thu, 29 Sep 2022 16:51:46 +0800 Subject: [PATCH 001/426] [Fix] fix some neo issues (#2599) * fix neo txid & asset id serialization missing zero leading issue * fix neo output reference not correct * fix TWCardanoGetStakingAddress not included issue * fix neo attribute serialize & deserialize not correct * fix some coding style and comments * use switch instead of if-else * use constants instead of number directly * add an empty line at the end of files --- src/NEO/CoinReference.h | 5 +- src/NEO/Constants.h | 16 +++++ src/NEO/Signer.cpp | 2 +- src/NEO/TransactionAttribute.h | 89 +++++++++++++++++++------ src/NEO/TransactionOutput.h | 13 ++-- tests/Cardano/TWCardanoAddressTests.cpp | 1 + tests/NEO/CoinReferenceTests.cpp | 8 +++ tests/NEO/TransactionAttributeTests.cpp | 16 +++-- tests/NEO/TransactionTests.cpp | 2 +- 9 files changed, 112 insertions(+), 40 deletions(-) create mode 100644 src/NEO/Constants.h diff --git a/src/NEO/CoinReference.h b/src/NEO/CoinReference.h index 1a7dd9fd120..8890f537d3d 100644 --- a/src/NEO/CoinReference.h +++ b/src/NEO/CoinReference.h @@ -19,6 +19,7 @@ class CoinReference : public Serializable { public: /// Number of bytes for prevIndex. static const size_t prevIndexSize = 2; + static const size_t prevHashSize = 32; uint256_t prevHash; uint16_t prevIndex = 0; @@ -35,7 +36,7 @@ class CoinReference : public Serializable { } Data serialize() const override { - auto resp = store(prevHash); + auto resp = store(prevHash, prevHashSize); encode16LE(prevIndex, resp); return resp; } @@ -46,4 +47,4 @@ class CoinReference : public Serializable { } }; -} \ No newline at end of file +} // namespace TW::NEO diff --git a/src/NEO/Constants.h b/src/NEO/Constants.h new file mode 100644 index 00000000000..817dc8840cf --- /dev/null +++ b/src/NEO/Constants.h @@ -0,0 +1,16 @@ +// Copyright © 2017-2022 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 + +namespace TW::NEO { + +static const size_t assetIdSize = 32; +static const size_t contractHashSize = 32; +static const size_t valueSize = 8; +static const size_t scriptHashSize = 20; + +} // namespace TW::NEO diff --git a/src/NEO/Signer.cpp b/src/NEO/Signer.cpp index 3329497f39f..1824af1b940 100644 --- a/src/NEO/Signer.cpp +++ b/src/NEO/Signer.cpp @@ -78,7 +78,7 @@ Proto::TransactionPlan Signer::plan(const Proto::SigningInput& input) { for (int i = 0; i < input.outputs_size(); i++) { auto* outputPlan = plan.add_outputs(); - if (available.find(input.inputs(i).asset_id()) == available.end() || + if (available.find(input.outputs(i).asset_id()) == available.end() || available[input.outputs(i).asset_id()] < input.outputs(i).amount()) { throw Common::Proto::SigningError(Common::Proto::Error_low_balance); } diff --git a/src/NEO/TransactionAttribute.h b/src/NEO/TransactionAttribute.h index be027e9bfd0..e1ac1235651 100644 --- a/src/NEO/TransactionAttribute.h +++ b/src/NEO/TransactionAttribute.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,49 +6,96 @@ #pragma once -#include "TransactionAttributeUsage.h" +#include "Constants.h" #include "ISerializable.h" #include "Serializable.h" +#include "TransactionAttributeUsage.h" #include "Data.h" namespace TW::NEO { class TransactionAttribute : public Serializable { - public: +public: TransactionAttributeUsage usage = TAU_ContractHash; Data _data; virtual ~TransactionAttribute() {} int64_t size() const override { - return 1 + _data.size(); + switch (usage) { + case TransactionAttributeUsage::TAU_ContractHash: + case TransactionAttributeUsage::TAU_ECDH02: + case TransactionAttributeUsage::TAU_ECDH03: + case TransactionAttributeUsage::TAU_Vote: + return 1 + contractHashSize; + case TransactionAttributeUsage::TAU_Script: + return 1 + scriptHashSize; + default: + if (usage >= TransactionAttributeUsage::TAU_Hash1 && + usage <= TransactionAttributeUsage::TAU_Hash15) { + return 1 + contractHashSize; + } + return 1 + varIntSize(_data.size()) + _data.size(); + } } void deserialize(const Data& data, int initial_pos = 0) override { if (static_cast(data.size()) < initial_pos + 1) { throw std::invalid_argument("Invalid data for deserialization"); } - usage = (TransactionAttributeUsage) data[initial_pos]; - if (usage == TransactionAttributeUsage::TAU_ContractHash || usage == TransactionAttributeUsage::TAU_Vote || - (usage >= TransactionAttributeUsage::TAU_Hash1 && usage <= TransactionAttributeUsage::TAU_Hash15)) { - _data = readBytes(data, 32, initial_pos + 1); - } else if (usage == TransactionAttributeUsage::TAU_ECDH02 || - usage == TransactionAttributeUsage::TAU_ECDH03) { - _data = readBytes(data, 32, initial_pos + 1); - } else if (usage == TransactionAttributeUsage::TAU_Script) { - _data = readBytes(data, 20, initial_pos + 1); - } else if (usage == TransactionAttributeUsage::TAU_DescriptionUrl) { - _data = readBytes(data, 1, initial_pos + 1); - } else if (usage == TransactionAttributeUsage::TAU_Description || - usage >= TransactionAttributeUsage::TAU_Remark) { - _data = readBytes(data, int(data.size()) - 1 - initial_pos, initial_pos + 1); - } else { + + // see: https://github.com/neo-project/neo/blob/v2.12.0/neo/Network/P2P/Payloads/TransactionAttribute.cs#L32 + usage = (TransactionAttributeUsage)data[initial_pos]; + switch (usage) { + case TransactionAttributeUsage::TAU_ECDH02: + case TransactionAttributeUsage::TAU_ECDH03: { + this->_data = concat({(TW::byte)usage}, readBytes(data, contractHashSize, initial_pos + 1)); + break; + } + + case TransactionAttributeUsage::TAU_Script: { + this->_data = readBytes(data, scriptHashSize, initial_pos + 1); + break; + } + + case TransactionAttributeUsage::TAU_DescriptionUrl: + case TransactionAttributeUsage::TAU_Description: + case TransactionAttributeUsage::TAU_Remark: { + this->_data = readVarBytes(data, initial_pos + 1); + break; + } + + default: + if (usage == TransactionAttributeUsage::TAU_ContractHash || + usage == TransactionAttributeUsage::TAU_Vote || + (usage >= TransactionAttributeUsage::TAU_Hash1 && usage <= TransactionAttributeUsage::TAU_Hash15)) { + this->_data = readBytes(data, contractHashSize, initial_pos + 1); + break; + } throw std::invalid_argument("TransactionAttribute Deserialize FormatException"); } } Data serialize() const override { - return concat(Data({static_cast(usage)}), _data); + Data result; + result.push_back((TW::byte)usage); + + // see: https://github.com/neo-project/neo/blob/v2.12.0/neo/Network/P2P/Payloads/TransactionAttribute.cs#L49 + if (usage == TransactionAttributeUsage::TAU_DescriptionUrl || + usage == TransactionAttributeUsage::TAU_Description || + usage >= TransactionAttributeUsage::TAU_Remark) { + Data resp; + encodeVarInt((uint64_t)_data.size(), resp); + result.insert(result.end(), resp.begin(), resp.end()); + } + if (usage == TransactionAttributeUsage::TAU_ECDH02 || + usage == TransactionAttributeUsage::TAU_ECDH03) { + result.insert(result.end(), _data.begin() + 1, _data.begin() + 1 + contractHashSize); + } else { + result.insert(result.end(), _data.begin(), _data.end()); + } + + return result; } bool operator==(const TransactionAttribute &other) const { @@ -58,4 +105,4 @@ class TransactionAttribute : public Serializable { } }; -} +} // namespace TW::NEO diff --git a/src/NEO/TransactionOutput.h b/src/NEO/TransactionOutput.h index dd99028a687..cc52b032301 100644 --- a/src/NEO/TransactionOutput.h +++ b/src/NEO/TransactionOutput.h @@ -7,6 +7,7 @@ #pragma once #include "../uint256.h" +#include "Constants.h" #include "Data.h" #include "../BinaryCoding.h" #include "ReadData.h" @@ -17,10 +18,6 @@ namespace TW::NEO { class TransactionOutput : public Serializable { public: - static const size_t assetIdSize = 32; - static const size_t valueSize = 8; - static const size_t scriptHashSize = 20; - uint256_t assetId; int64_t value = 0; uint256_t scriptHash; @@ -28,7 +25,7 @@ class TransactionOutput : public Serializable { virtual ~TransactionOutput() {} int64_t size() const override { - return store(assetId).size() + valueSize + store(scriptHash).size(); + return store(assetId, assetIdSize).size() + valueSize + store(scriptHash, scriptHashSize).size(); } void deserialize(const Data& data, int initial_pos = 0) override { @@ -38,9 +35,9 @@ class TransactionOutput : public Serializable { } Data serialize() const override { - auto resp = store(assetId); + auto resp = store(assetId, assetIdSize); encode64LE(value, resp); - return concat(resp, store(scriptHash)); + return concat(resp, store(scriptHash, scriptHashSize)); } bool operator==(const TransactionOutput &other) const { @@ -50,4 +47,4 @@ class TransactionOutput : public Serializable { } }; -} \ No newline at end of file +} // namespace TW::NEO diff --git a/tests/Cardano/TWCardanoAddressTests.cpp b/tests/Cardano/TWCardanoAddressTests.cpp index 33af5be338b..4ce62f4e01a 100644 --- a/tests/Cardano/TWCardanoAddressTests.cpp +++ b/tests/Cardano/TWCardanoAddressTests.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include diff --git a/tests/NEO/CoinReferenceTests.cpp b/tests/NEO/CoinReferenceTests.cpp index 90c1f2700dc..1101986a96f 100644 --- a/tests/NEO/CoinReferenceTests.cpp +++ b/tests/NEO/CoinReferenceTests.cpp @@ -21,6 +21,14 @@ TEST(NEOCoinReference, Serialize) { EXPECT_EQ(prevHash + "0100", hex(coinReference.serialize())); } +TEST(NEOCoinReference, SerializeWithZeroLeading) { + auto coinReference = CoinReference(); + string prevHash = "0037ebf259ca5c6c43a5e7117c910858ea1146290e07d39e48554bc00d890b94"; + coinReference.prevHash = load(parse_hex(prevHash)); + coinReference.prevIndex = 1; + EXPECT_EQ(prevHash + "0100", hex(coinReference.serialize())); +} + TEST(NEOCoinReference, Deserialize) { auto coinReference = CoinReference(); coinReference.deserialize(parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a0100")); diff --git a/tests/NEO/TransactionAttributeTests.cpp b/tests/NEO/TransactionAttributeTests.cpp index d911270c92f..73b64b14554 100644 --- a/tests/NEO/TransactionAttributeTests.cpp +++ b/tests/NEO/TransactionAttributeTests.cpp @@ -28,7 +28,9 @@ TEST(NEOTransactionAttribute, Serialize) { EXPECT_EQ("30" + data, hex(transactionAttribute.serialize())); transactionAttribute.usage = TransactionAttributeUsage::TAU_ECDH02; - transactionAttribute._data = parse_hex(data); + transactionAttribute._data = {(TW::byte)transactionAttribute.usage}; + auto d = parse_hex(data); + transactionAttribute._data.insert(transactionAttribute._data.end(), d.begin(), d.end()); EXPECT_EQ("02" + data, hex(transactionAttribute.serialize())); data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af"; @@ -39,12 +41,12 @@ TEST(NEOTransactionAttribute, Serialize) { data = "bd"; transactionAttribute.usage = TransactionAttributeUsage::TAU_DescriptionUrl; transactionAttribute._data = parse_hex(data); - EXPECT_EQ("81" + data, hex(transactionAttribute.serialize())); + EXPECT_EQ("8101" + data, hex(transactionAttribute.serialize())); data = "bdecbb623eee6f9ade28d5a8ff5fb3ea"; transactionAttribute.usage = TransactionAttributeUsage::TAU_Remark; transactionAttribute._data = parse_hex(data); - EXPECT_EQ("f0" + data, hex(transactionAttribute.serialize())); + EXPECT_EQ("f010" + data, hex(transactionAttribute.serialize())); } TEST(NEOTransactionAttribute, Deserialize) { @@ -61,7 +63,7 @@ TEST(NEOTransactionAttribute, Deserialize) { transactionAttribute.deserialize(parse_hex("02" + data)); EXPECT_EQ(TransactionAttributeUsage::TAU_ECDH02, transactionAttribute.usage); - EXPECT_EQ(data, hex(transactionAttribute._data)); + EXPECT_EQ("02" + data, hex(transactionAttribute._data)); data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af"; transactionAttribute.deserialize(parse_hex("20" + data)); @@ -69,12 +71,12 @@ TEST(NEOTransactionAttribute, Deserialize) { EXPECT_EQ(data, hex(transactionAttribute._data)); data = "bd"; - transactionAttribute.deserialize(parse_hex("81" + data)); + transactionAttribute.deserialize(parse_hex("8101" + data)); EXPECT_EQ(TransactionAttributeUsage::TAU_DescriptionUrl, transactionAttribute.usage); EXPECT_EQ(data, hex(transactionAttribute._data)); data = "bdecbb623eee6f9ade28d5a8ff5fb3ea"; - transactionAttribute.deserialize(parse_hex("f0" + data)); + transactionAttribute.deserialize(parse_hex("f010" + data)); EXPECT_EQ(TransactionAttributeUsage::TAU_Remark, transactionAttribute.usage); EXPECT_EQ(data, hex(transactionAttribute._data)); @@ -84,8 +86,8 @@ TEST(NEOTransactionAttribute, Deserialize) { TEST(NEOTransactionAttribute, DeserializeInitialPositionAfterData) { auto transactionAttribute = TransactionAttribute(); EXPECT_THROW(transactionAttribute.deserialize(Data(), 1), std::invalid_argument); - EXPECT_THROW(transactionAttribute.deserialize(Data({1}), 2), std::invalid_argument); } + } // namespace TW::NEO::tests diff --git a/tests/NEO/TransactionTests.cpp b/tests/NEO/TransactionTests.cpp index 08c328ea405..fc973084e90 100644 --- a/tests/NEO/TransactionTests.cpp +++ b/tests/NEO/TransactionTests.cpp @@ -62,7 +62,7 @@ TEST(NEOTransaction, SerializeDeserializeAttribute) { transaction.attributes.push_back(TransactionAttribute()); transaction.attributes[1].usage = TransactionAttributeUsage::TAU_ECDH02; - transaction.attributes[1]._data = parse_hex("b7ecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"); + transaction.attributes[1]._data = parse_hex("02b7ecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"); serialized = transaction.serialize(); const string twoVarLong = "02"; string expectedSerialized = "8007" + twoVarLong; From e446d8675017a5f92277eaef51a8ff16fa6b715f Mon Sep 17 00:00:00 2001 From: wh Date: Fri, 30 Sep 2022 18:06:56 +0800 Subject: [PATCH 002/426] [Fio] Fix `signatureToBase58` typo (#2606) * fix a typo of fio * fix signatureToBase58 issue --- src/FIO/Signer.cpp | 2 +- src/FIO/Signer.h | 2 +- src/FIO/TransactionBuilder.cpp | 2 +- tests/FIO/SignerTests.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FIO/Signer.cpp b/src/FIO/Signer.cpp index 19b1803f624..e4390dd3b44 100644 --- a/src/FIO/Signer.cpp +++ b/src/FIO/Signer.cpp @@ -40,7 +40,7 @@ Data Signer::signData(const PrivateKey& privKey, const Data& data) { return signature; } -std::string Signer::signatureToBsase58(const Data& sig) { +std::string Signer::signatureToBase58(const Data& sig) { Data sigWithSuffix(sig); append(sigWithSuffix, TW::data(SignatureSuffix)); // take hash, ripemd, first 4 bytes diff --git a/src/FIO/Signer.h b/src/FIO/Signer.h index c41b072ed72..7e0800d71d9 100644 --- a/src/FIO/Signer.h +++ b/src/FIO/Signer.h @@ -30,7 +30,7 @@ class Signer { static Data signData(const PrivateKey& privKey, const Data& data); /// Used internally, encode signature to base58 with prefix. Ex.: "SIG_K1_K54CA1jmhgWrSdvrNrkokPyvqh7dwsSoQHNU9xgD3Ezf6cJySzhKeUubVRqmpYdnjoP1DM6SorroVAgrCu3qqvJ9coAQ6u" - static std::string signatureToBsase58(const Data& sig); + static std::string signatureToBase58(const Data& sig); /// Verify a signature, used in testing static bool verify(const PublicKey& pubKey, const Data& data, const Data& signature); diff --git a/src/FIO/TransactionBuilder.cpp b/src/FIO/TransactionBuilder.cpp index c0203ec9911..1c5fa2b4d8b 100644 --- a/src/FIO/TransactionBuilder.cpp +++ b/src/FIO/TransactionBuilder.cpp @@ -240,7 +240,7 @@ string TransactionBuilder::signAdnBuildTx(const Data& chainId, const Data& packe Data sigBuf(chainId); append(sigBuf, packedTx); append(sigBuf, TW::Data(32)); // context_free - string signature = Signer::signatureToBsase58(Signer::signData(privateKey, sigBuf)); + string signature = Signer::signatureToBase58(Signer::signData(privateKey, sigBuf)); // Build json json tx = { diff --git a/tests/FIO/SignerTests.cpp b/tests/FIO/SignerTests.cpp index e85ab9422d5..871db8d8091 100644 --- a/tests/FIO/SignerTests.cpp +++ b/tests/FIO/SignerTests.cpp @@ -18,7 +18,7 @@ namespace TW::FIO::tests { using namespace std; TEST(FIOSigner, SignEncode) { - string sig1 = Signer::signatureToBsase58(parse_hex("1f4fccc30bcba876963aef6de584daf7258306c02f4528fe25b116b517de8b349968bdc080cd6bef36f5a46d31a7c01ed0806ad215bb66a94f61e27a895d610983")); + string sig1 = Signer::signatureToBase58(parse_hex("1f4fccc30bcba876963aef6de584daf7258306c02f4528fe25b116b517de8b349968bdc080cd6bef36f5a46d31a7c01ed0806ad215bb66a94f61e27a895d610983")); EXPECT_EQ("SIG_K1_K5hJTPeiV4bDkNR13mf66N2DY5AtVL4NU1iWE4G4dsczY2q68oCcUVxhzFdxjgV2eAeb2jNV1biqtCJ3SaNL8kkNgoZ43H", sig1); } @@ -37,7 +37,7 @@ TEST(FIOSigner, SignInternals) { Data sign2 = Signer::signData(pk, rawData); EXPECT_EQ("1f4ae8d1b993f94d0de4b249d5185481770de0711863ad640b3aac21de598fcc02761c6e5395106bafb7b09aab1c7aa5ac0573dbd821c2d255725391a5105d30d1", hex(sign2)); - string sigStr = Signer::signatureToBsase58(sign2); + string sigStr = Signer::signatureToBase58(sign2); EXPECT_EQ("SIG_K1_K54CA1jmhgWrSdvrNrkokPyvqh7dwsSoQHNU9xgD3Ezf6cJySzhKeUubVRqmpYdnjoP1DM6SorroVAgrCu3qqvJ9coAQ6u", sigStr); EXPECT_TRUE(Signer::verify(pk.getPublicKey(TWPublicKeyTypeSECP256k1), hash, sign2)); } From 4efd4be01e6e49887d7915766a45be908ae8b169 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Fri, 30 Sep 2022 23:30:46 +0200 Subject: [PATCH 003/426] New swift test for external signature feature (#2607) --- swift/Tests/TransactionCompilerTests.swift | 176 ++++++++++++++++++ tests/TransactionCompilerTests.cpp | 8 +- .../interface/TWTransactionCompilerTests.cpp | 2 +- 3 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 swift/Tests/TransactionCompilerTests.swift diff --git a/swift/Tests/TransactionCompilerTests.swift b/swift/Tests/TransactionCompilerTests.swift new file mode 100644 index 00000000000..b5e147a65f5 --- /dev/null +++ b/swift/Tests/TransactionCompilerTests.swift @@ -0,0 +1,176 @@ +// Copyright © 2017-2022 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. + +import XCTest +import WalletCore + +class TransactionCompilerTests: XCTestCase { + override func setUp() { + continueAfterFailure = false + } + + func testBitcoinCompileWithSignatures() throws { + // Test external signining with a Bitcoin transaction with 3 input UTXOs, all used, but only using 2 public keys. + // Three signatures are neeeded. This illustrates that order of UTXOs/hashes is not always the same. + + let revUtxoHash0 = Data(hexString: "07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa8")! + let revUtxoHash1 = Data(hexString: "d6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e")! + let revUtxoHash2 = Data(hexString: "6021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d")! + let inPubKey0 = Data(hexString: "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382")! + let inPubKey1 = Data(hexString: "0217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc")! + let inPubKeyHash0 = Data(hexString: "bd92088bb7e82d611a9b94fbb74a0908152b784f")! + let inPubKeyHash1 = Data(hexString: "6641abedacf9483b793afe1718689cc9420bbb1c")! + + // Test data: Input UTXO infos + struct UtxoInfo { + let revUtxoHash: Data + let publicKey: Data + let amount: Int64 + let index: UInt32 + } + let utxoInfos: [UtxoInfo] = [ + // first + UtxoInfo(revUtxoHash: revUtxoHash0, publicKey: inPubKey0, amount: 600000, index: 0), + // second UTXO, with same pubkey + UtxoInfo(revUtxoHash: revUtxoHash1, publicKey: inPubKey0, amount: 500000, index: 1), + // third UTXO, with different pubkey + UtxoInfo(revUtxoHash: revUtxoHash2, publicKey: inPubKey1, amount: 400000, index: 0), + ] + + // Signature infos, indexed by pubkeyhash+hash + struct SignatureInfo { + let signature: Data + let publicKey: Data + } + let signatureInfos: [String: SignatureInfo] = [ + inPubKeyHash0.hexString + "+" + "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7": + SignatureInfo(signature: Data(hexString: "304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a40")!, publicKey: inPubKey0), + inPubKeyHash1.hexString + "+" + "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6": + SignatureInfo(signature: Data(hexString: "3044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e4")!, publicKey: inPubKey1), + inPubKeyHash0.hexString + "+" + "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101": + SignatureInfo(signature: Data(hexString: "30440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc")!, publicKey: inPubKey0), + ] + + let coin = CoinType.bitcoin + let ownAddress = "bc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z00ppggv" + + // Setup input for Plan + var signingInput = BitcoinSigningInput.with { + $0.coinType = coin.rawValue + $0.hashType = BitcoinSigHashType.all.rawValue + $0.amount = 1200000 + $0.useMaxAmount = false + $0.byteFee = 1 + $0.toAddress = "bc1q2dsdlq3343vk29runkgv4yc292hmq53jedfjmp" + $0.changeAddress = ownAddress + } + + // process UTXOs + var count: UInt32 = 0 + for u in utxoInfos { + let publicKey = PublicKey(data: u.publicKey, type: PublicKeyType.secp256k1) + let address = SegwitAddress(hrp: .bitcoin, publicKey: publicKey!) + if (count == 0) { XCTAssertEqual(address.description, ownAddress) } + if (count == 1) { XCTAssertEqual(address.description, ownAddress) } + if (count == 2) { XCTAssertEqual(address.description, "bc1qveq6hmdvl9yrk7f6lct3s6yue9pqhwcuxedggg") } + + let utxoScript = BitcoinScript.lockScriptForAddress(address: address.description, coin: coin) + if (count == 0) { XCTAssertEqual(utxoScript.data.hexString, "0014bd92088bb7e82d611a9b94fbb74a0908152b784f") } + if (count == 1) { XCTAssertEqual(utxoScript.data.hexString, "0014bd92088bb7e82d611a9b94fbb74a0908152b784f") } + if (count == 2) { XCTAssertEqual(utxoScript.data.hexString, "00146641abedacf9483b793afe1718689cc9420bbb1c") } + + let keyHash: Data = utxoScript.matchPayToWitnessPublicKeyHash()! + if (count == 0) { XCTAssertEqual(keyHash.hexString, inPubKeyHash0.hexString) } + if (count == 1) { XCTAssertEqual(keyHash.hexString, inPubKeyHash0.hexString) } + if (count == 2) { XCTAssertEqual(keyHash.hexString, inPubKeyHash1.hexString) } + + let redeemScript = BitcoinScript.buildPayToPublicKeyHash(hash: keyHash) + if (count == 0) { XCTAssertEqual(redeemScript.data.hexString, "76a914bd92088bb7e82d611a9b94fbb74a0908152b784f88ac") } + if (count == 1) { XCTAssertEqual(redeemScript.data.hexString, "76a914bd92088bb7e82d611a9b94fbb74a0908152b784f88ac") } + if (count == 2) { XCTAssertEqual(redeemScript.data.hexString, "76a9146641abedacf9483b793afe1718689cc9420bbb1c88ac") } + signingInput.scripts[keyHash.hexString] = redeemScript.data + + let outPoint = BitcoinOutPoint.with { + $0.hash = u.revUtxoHash + $0.index = u.index + $0.sequence = UInt32.max + } + let utxo = BitcoinUnspentTransaction.with { + $0.script = utxoScript.data + $0.amount = u.amount + $0.outPoint = outPoint + } + signingInput.utxo.append(utxo) + + count += 1 + } + XCTAssertEqual(count, 3) + XCTAssertEqual(signingInput.utxo.count, 3) + + // Plan + let plan: BitcoinTransactionPlan = AnySigner.plan(input: signingInput, coin: coin) + + // At this point plan can be checked, assume it is accepted unmodified + XCTAssertEqual(plan.amount, 1200000) + XCTAssertEqual(plan.fee, 277) + XCTAssertEqual(plan.change, 299723) + XCTAssertEqual(plan.utxos.count, 3) + // Note that UTXOs happen to be in reverse order compared to the input + XCTAssertEqual(plan.utxos[0].outPoint.hash.hexString, revUtxoHash2.hexString) + XCTAssertEqual(plan.utxos[1].outPoint.hash.hexString, revUtxoHash1.hexString) + XCTAssertEqual(plan.utxos[2].outPoint.hash.hexString, revUtxoHash0.hexString) + + // Extend input with accepted plan + signingInput.plan = plan + + // Serialize input + let txInputData = try signingInput.serializedData() + XCTAssertEqual(txInputData.count, 692) + + /// Step 2: Obtain preimage hashes + let preImageHashes = TransactionCompiler.preImageHashes(coinType: coin, txInputData: txInputData) + let preSigningOutput: BitcoinPreSigningOutput = try BitcoinPreSigningOutput(serializedData: preImageHashes) + + XCTAssertEqual(preSigningOutput.error, CommonSigningError.ok) + XCTAssertEqual(preSigningOutput.hashPublicKeys[0].dataHash.hexString, "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6") + XCTAssertEqual(preSigningOutput.hashPublicKeys[1].dataHash.hexString, "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7") + XCTAssertEqual(preSigningOutput.hashPublicKeys[2].dataHash.hexString, "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101") + XCTAssertEqual(preSigningOutput.hashPublicKeys[0].publicKeyHash.hexString, inPubKeyHash1.hexString) + XCTAssertEqual(preSigningOutput.hashPublicKeys[1].publicKeyHash.hexString, inPubKeyHash0.hexString) + XCTAssertEqual(preSigningOutput.hashPublicKeys[2].publicKeyHash.hexString, inPubKeyHash0.hexString) + + // Simulate signatures, normally they are obtained from external source, e.g. a signature server. + var signatureVec = DataVector() + var pubkeyVec = DataVector() + for h in preSigningOutput.hashPublicKeys { + let preImageHash = h.dataHash + let pubkeyHash = h.publicKeyHash + + let key: String = pubkeyHash.hexString + "+" + preImageHash.hexString + XCTAssertTrue(signatureInfos.contains { $0.key == key }) + let sigInfo: SignatureInfo = signatureInfos[key]! + let publicKeyData = sigInfo.publicKey + let publicKey = PublicKey(data: publicKeyData, type: PublicKeyType.secp256k1) + let signature = sigInfo.signature + + signatureVec.add(data: signature) + pubkeyVec.add(data: publicKeyData) + + // Verify signature (pubkey & hash & signature) + publicKey?.verifyAsDER(signature: signature, message: preImageHash) + } + + /// Step 3: Compile transaction info + let compileWithSignatures = TransactionCompiler.compileWithSignatures(coinType: coin, txInputData: txInputData, signatures: signatureVec, publicKeys: pubkeyVec) + + let ExpectedTx = "010000000001036021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d0000000000ffffffffd6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e0100000000ffffffff07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa80000000000ffffffff02804f1200000000001600145360df8231ac5965147c9d90ca930a2aafb05232cb92040000000000160014bd92088bb7e82d611a9b94fbb74a0908152b784f02473044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e401210217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc0247304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a400121024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382024730440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc0121024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb49338200000000" + + XCTAssertEqual(compileWithSignatures.count, 786) + let output: BitcoinSigningOutput = try BitcoinSigningOutput(serializedData: compileWithSignatures) + XCTAssertEqual(output.encoded.count, 518) + XCTAssertEqual(output.encoded.hexString, ExpectedTx) + } +} diff --git a/tests/TransactionCompilerTests.cpp b/tests/TransactionCompilerTests.cpp index 0d9c8e4eab8..9590eb07ef6 100644 --- a/tests/TransactionCompilerTests.cpp +++ b/tests/TransactionCompilerTests.cpp @@ -99,7 +99,7 @@ TEST(TransactionCompiler, BinanceCompileWithSignatures) { } TEST(TransactionCompiler, BitcoinCompileWithSignatures) { - // Test external signining with a Bircoin transaction with 3 input UTXOs, all used, but only using 2 public keys. + // Test external signining with a Bitcoin transaction with 3 input UTXOs, all used, but only using 2 public keys. // Three signatures are neeeded. This illustrates that order of UTXOs/hashes is not always the same. const auto revUtxoHash0 = parse_hex("07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa8"); @@ -110,7 +110,7 @@ TEST(TransactionCompiler, BitcoinCompileWithSignatures) { const auto inPubKeyHash0 = parse_hex("bd92088bb7e82d611a9b94fbb74a0908152b784f"); const auto inPubKeyHash1 = parse_hex("6641abedacf9483b793afe1718689cc9420bbb1c"); - // Input UTXO infos + // Test data: Input UTXO infos struct UtxoInfo { Data revUtxoHash; Data publicKey; @@ -210,7 +210,7 @@ TEST(TransactionCompiler, BitcoinCompileWithSignatures) { Bitcoin::Proto::TransactionPlan plan; ANY_PLAN(signingInput, plan, coin); - // Plan is checked, assume it is accepted + // At this point plan can be checked, assume it is accepted unmodified EXPECT_EQ(plan.amount(), 1'200'000); EXPECT_EQ(plan.fee(), 277); EXPECT_EQ(plan.change(), 299'723); @@ -240,7 +240,7 @@ TEST(TransactionCompiler, BitcoinCompileWithSignatures) { EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[1].public_key_hash()), hex(inPubKeyHash0)); EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[2].public_key_hash()), hex(inPubKeyHash0)); - // Simulate signatures, normally obtained from signature server. + // Simulate signatures, normally they are obtained from external source, e.g. a signature server. std::vector signatureVec; std::vector pubkeyVec; for (const auto& h: preSigningOutput.hash_public_keys()) { diff --git a/tests/interface/TWTransactionCompilerTests.cpp b/tests/interface/TWTransactionCompilerTests.cpp index 5793a97340a..20cab4a754e 100644 --- a/tests/interface/TWTransactionCompilerTests.cpp +++ b/tests/interface/TWTransactionCompilerTests.cpp @@ -199,7 +199,7 @@ Data dataFromTWData(std::shared_ptr data) { } TEST(TWTransactionCompiler, ExternalSignatureSignBitcoin) { - // Test external signining with a Bircoin transaction with 3 input UTXOs, all used, but only using 2 public keys. + // Test external signining with a Bitcoin transaction with 3 input UTXOs, all used, but only using 2 public keys. // Three signatures are neeeded. This illustrates that order of UTXOs/hashes is not always the same. const auto revUtxoHash0 = parse_hex("07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa8"); From 0ea61af0ae2f0a61cdc868126504df855a105d05 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 3 Oct 2022 13:31:40 +0200 Subject: [PATCH 004/426] [Aptos]: Add Aptos chain (#2595) --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../app/blockchains/aptos/TestAptosAddress.kt | 28 +++ .../app/blockchains/aptos/TestAptosSigner.kt | 58 +++++ docs/registry.md | 1 + include/TrustWalletCore/TWBlockchain.h | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 30 +++ src/Aptos/Address.cpp | 88 +++++++ src/Aptos/Address.h | 58 +++++ src/Aptos/Entry.cpp | 26 ++ src/Aptos/Entry.h | 22 ++ src/Aptos/MoveTypes.cpp | 146 +++++++++++ src/Aptos/MoveTypes.h | 101 ++++++++ src/Aptos/Signer.cpp | 115 +++++++++ src/Aptos/Signer.h | 27 ++ src/Aptos/TransactionBuilder.h | 100 ++++++++ src/Aptos/TransactionPayload.cpp | 57 +++++ src/Aptos/TransactionPayload.h | 46 ++++ src/BCS.cpp | 42 ++++ src/BCS.h | 222 +++++++++++++++++ src/Coin.cpp | 3 + src/concepts/tw_concepts.h | 19 ++ src/proto/Aptos.proto | 92 +++++++ swift/Tests/Blockchains/AptosTests.swift | 48 ++++ swift/Tests/CoinAddressDerivationTests.swift | 3 + tests/Aptos/AddressTests.cpp | 54 ++++ tests/Aptos/MoveTypesTests.cpp | 59 +++++ tests/Aptos/SignerTests.cpp | 231 ++++++++++++++++++ tests/Aptos/TWAnySignerTests.cpp | 63 +++++ tests/Aptos/TWAptosAddressTests.cpp | 34 +++ tests/Aptos/TWCoinTypeTests.cpp | 35 +++ tests/Aptos/TransactionPayloadTests.cpp | 32 +++ tests/BCSTests.cpp | 165 +++++++++++++ tests/Cardano/TWCardanoAddressTests.cpp | 1 + tests/CoinAddressDerivationTests.cpp | 3 + tests/HDWallet/HDWalletTests.cpp | 11 + 36 files changed, 2023 insertions(+) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt create mode 100644 src/Aptos/Address.cpp create mode 100644 src/Aptos/Address.h create mode 100644 src/Aptos/Entry.cpp create mode 100644 src/Aptos/Entry.h create mode 100644 src/Aptos/MoveTypes.cpp create mode 100644 src/Aptos/MoveTypes.h create mode 100644 src/Aptos/Signer.cpp create mode 100644 src/Aptos/Signer.h create mode 100644 src/Aptos/TransactionBuilder.h create mode 100644 src/Aptos/TransactionPayload.cpp create mode 100644 src/Aptos/TransactionPayload.h create mode 100644 src/BCS.cpp create mode 100644 src/BCS.h create mode 100644 src/concepts/tw_concepts.h create mode 100644 src/proto/Aptos.proto create mode 100644 swift/Tests/Blockchains/AptosTests.swift create mode 100644 tests/Aptos/AddressTests.cpp create mode 100644 tests/Aptos/MoveTypesTests.cpp create mode 100644 tests/Aptos/SignerTests.cpp create mode 100644 tests/Aptos/TWAnySignerTests.cpp create mode 100644 tests/Aptos/TWAptosAddressTests.cpp create mode 100644 tests/Aptos/TWCoinTypeTests.cpp create mode 100644 tests/Aptos/TransactionPayloadTests.cpp create mode 100644 tests/BCSTests.cpp 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 55d6d14dd38..34765728c77 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 @@ -105,5 +105,6 @@ class CoinAddressDerivationTests { NATIVEEVMOS -> assertEquals("evmos13u6g7vqgw074mgmf2ze2cadzvkz9snlwstd20d", address) NERVOS -> assertEquals("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3", address) EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) + APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt new file mode 100644 index 00000000000..180617ff5b1 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 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.aptos + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestAptosAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val any = AnyAddress("0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108", CoinType.APTOS) + assertEquals(any.coin(), CoinType.APTOS) + assertEquals(any.description(), "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108") + + assertFalse(AnyAddress.isValid("0xMQqpqMQgCBuiPkoXfgZZsJvuzCeI1zc00z6vHJj4", CoinType.APTOS)) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt new file mode 100644 index 00000000000..32df8c8e33e --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt @@ -0,0 +1,58 @@ +// Copyright © 2017-2022 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.aptos + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHexBytesInByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType +import wallet.core.jni.proto.Aptos + +class TestAptosSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun AptosTransactionSigning() { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xbb3b3c33781c27e486afa2db854fb0a5c846d0967672feb2c6c3297a2b14e1ce?network=Devnet + val key = + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec".toHexBytesInByteString() + + val transfer = Aptos.TransferMessage.newBuilder().setAmount(1000) + .setTo("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").build() + val signingInput = Aptos.SigningInput.newBuilder().setChainId(32) + .setSender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30") + .setSequenceNumber(15) + .setGasUnitPrice(100) + .setMaxGasAmount(3296766) + .setExpirationTimestampSecs(3664390082) + .setTransfer(transfer) + .setPrivateKey(key) + .build() + + val result = AnySigner.sign(signingInput, CoinType.APTOS, Aptos.SigningOutput.parser()) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.rawTxn.toByteArray())), + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000020" + ) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.authenticator.signature.toByteArray())), + "2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05" + ) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.encoded.toByteArray())), + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c402ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05" + ) + } +} diff --git a/docs/registry.md b/docs/registry.md index 755c49365ac..2bc175c03ad 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -50,6 +50,7 @@ This list is generated from [./registry.json](../registry.json) | 500 | Theta | THETA | | | | 501 | Solana | SOL | | | | 508 | Elrond | eGLD | | | +| 637 | Aptos | APT | | | | 714 | BNB Beacon Chain | BNB | | | | 818 | VeChain | VET | | | | 820 | Callisto | CLO | | | diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index eb289fc6d43..80fe59660ce 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -54,6 +54,7 @@ enum TWBlockchain { TWBlockchainKusama = 40, // Polkadot TWBlockchainNervos = 41, TWBlockchainEverscale = 42, + TWBlockchainAptos = 43, // Aptos }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 002b622c28a..831f9c3124a 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -116,6 +116,7 @@ enum TWCoinType { TWCoinTypeOKXChain = 996, TWCoinTypeNervos = 309, TWCoinTypeEverscale = 396, + TWCoinTypeAptos = 637, }; /// Returns the blockchain for a coin type. diff --git a/registry.json b/registry.json index 537498c91ac..2c8dcb73ebe 100644 --- a/registry.json +++ b/registry.json @@ -393,6 +393,36 @@ "documentation": "https://www.icondev.io/docs/icon-json-rpc-v3" } }, + { + "id": "aptos", + "name": "Aptos", + "displayName": "Aptos", + "coinId": 637, + "symbol": "APT", + "decimals": 8, + "chainId": "1", + "blockchain": "Aptos", + "derivation": [ + { + "path": "m/44'/637'/0'/0'/0'" + } + ], + "curve": "ed25519", + "publicKeyType": "ed25519", + "explorer": { + "url": "https://explorer.aptoslabs.com", + "txPath": "/txn/", + "accountPath": "/account/", + "sampleTx": "91424546", + "sampleAccount": "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108" + }, + "info": { + "url": "https://aptoslabs.com/", + "source": "https://github.com/aptos-labs/aptos-core", + "rpc": "https://fullnode.devnet.aptoslabs.com", + "documentation": "https://fullnode.devnet.aptoslabs.com/v1/spec#/" + } + }, { "id": "cosmos", "name": "Cosmos", diff --git a/src/Aptos/Address.cpp b/src/Aptos/Address.cpp new file mode 100644 index 00000000000..30c1b587872 --- /dev/null +++ b/src/Aptos/Address.cpp @@ -0,0 +1,88 @@ +// Copyright © 2017-2022 Trust Wallet. +// Author: Clement Doumergue +// +// 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 "HexCoding.h" + +namespace { + +std::string normalize(const std::string& string, std::size_t hexLen) { + std::string hexStr((TW::Aptos::Address::size * 2) - hexLen, '0'); + hexStr.append(string); + return hexStr; +} + +} // namespace + +namespace TW::Aptos { + +bool Address::isValid(const std::string& string) { + auto address = string; + if (address.starts_with("0x")) { + address = address.substr(2); + if (std::size_t hexLen = address.size(); hexLen < Address::size * 2) { + address = normalize(address, hexLen); + } + } + if (address.size() != 2 * Address::size) { + return false; + } + const auto data = parse_hex(address); + return isValid(data); +} + +Address::Address(const std::string& string) { + if (!isValid(string)) { + throw std::invalid_argument("Invalid address string"); + } + auto hexFunctor = [&string]() { + if (std::size_t hexLen = string.size() - 2; string.starts_with("0x") && hexLen < Address::size * 2) { + //! We have specific address like 0x1, padding it. + return parse_hex(normalize(string.substr(2), hexLen)); + } else { + return parse_hex(string); + } + }; + + const auto data = hexFunctor(); + std::copy(data.begin(), 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()); +} + +Address::Address(const PublicKey& publicKey) { + if (publicKey.type != TWPublicKeyTypeED25519) { + throw std::invalid_argument("Invalid public key type"); + } + auto key_data = publicKey.bytes; + append(key_data, 0x00); + const auto data = Hash::sha3_256(key_data); + std::copy(data.begin(), data.end(), bytes.begin()); +} + +std::string Address::string(bool withPrefix) const { + std::string output = withPrefix ? "0x" : ""; + return output + hex(bytes); +} + +std::string Address::shortString() const { + std::string s = hex(bytes); + s.erase(0, s.find_first_not_of('0')); + return s; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, Address addr) noexcept { + stream.add_bytes(addr.bytes.begin(), addr.bytes.end()); + return stream; +} + +} // namespace TW::Aptos diff --git a/src/Aptos/Address.h b/src/Aptos/Address.h new file mode 100644 index 00000000000..52b995fa3be --- /dev/null +++ b/src/Aptos/Address.h @@ -0,0 +1,58 @@ +// Copyright © 2017-2022 Trust Wallet. +// Author: Clement Doumergue +// +// 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 "BCS.h" +#include "Data.h" +#include "../PublicKey.h" + +#include + +namespace TW::Aptos { + +class Address { +public: + static constexpr size_t size = 32; + + std::array bytes; + + /// Determines whether a collection of bytes makes a valid address. + static bool isValid(const Data& data) { return data.size() == size; } + + /// Determines whether a string makes a valid address. + static bool isValid(const std::string& string); + + /// Initializes an Aptos address with a string representation. + explicit Address(const std::string& string); + + /// Initializes an Aptos address with a collection of bytes + explicit Address(const Data& data); + + /// Initializes an Aptos address with a public key. + explicit Address(const PublicKey& publicKey); + + /// Constructor that allow factory programming; + Address() noexcept = default; + + /// Returns a string representation of the address. + [[nodiscard]] std::string string(bool withPrefix = true) const; + + /// Returns a short string representation of the address. E.G 0x1; + [[nodiscard]] std::string shortString() const; +}; + +constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcept { + return lhs.bytes == rhs.bytes; +} + +inline const Address gAddressZero = Address("0x0"); +inline const Address gAddressOne = Address("0x1"); + +BCS::Serializer& operator<<(BCS::Serializer& stream, Address) noexcept; + +} // namespace TW::Aptos diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp new file mode 100644 index 00000000000..4c299f946a2 --- /dev/null +++ b/src/Aptos/Entry.cpp @@ -0,0 +1,26 @@ +// Copyright © 2017-2022 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" + +namespace TW::Aptos { + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { + return Address::isValid(address); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { + return Address(publicKey).string(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +} // namespace TW::Aptos diff --git a/src/Aptos/Entry.h b/src/Aptos/Entry.h new file mode 100644 index 00000000000..0fd1ab1d008 --- /dev/null +++ b/src/Aptos/Entry.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 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::Aptos { + +/// Entry point for implementation of Aptos coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; +}; + +} // namespace TW::Aptos diff --git a/src/Aptos/MoveTypes.cpp b/src/Aptos/MoveTypes.cpp new file mode 100644 index 00000000000..884df19ec10 --- /dev/null +++ b/src/Aptos/MoveTypes.cpp @@ -0,0 +1,146 @@ +// Copyright © 2017-2022 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 +#include + +namespace TW::Aptos { + +Aptos::ModuleId::ModuleId(Address accountAddress, Identifier name) noexcept + : mAccountAddress(accountAddress), mName(std::move(name)) { +} + +Data ModuleId::accessVector() const noexcept { + BCS::Serializer serializer; + serializer << static_cast(gCodeTag) << mAccountAddress << mName; + return serializer.bytes; +} + +std::string ModuleId::string() const noexcept { + std::stringstream ss; + ss << mAccountAddress.string() << "::" << mName; + return ss.str(); +} + +std::string ModuleId::shortString() const noexcept { + std::stringstream ss; + ss << "0x" << mAccountAddress.shortString() << "::" << mName; + return ss.str(); +} + +Data StructTag::serialize(bool withResourceTag) const noexcept { + BCS::Serializer serializer; + if (withResourceTag) + { + serializer << gResourceTag; + } + serializer << mAccountAddress << mModule << mName << mTypeParams; + return serializer.bytes; +} + +StructTag::StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept + : mAccountAddress(accountAddress), mModule(std::move(module)), mName(std::move(name)), mTypeParams(std::move(typeParams)) { +} +std::string StructTag::string() const noexcept { + std::stringstream ss; + ss << "0x" << mAccountAddress.shortString() << "::" << mModule << "::" << mName; + if (!mTypeParams.empty()) { + ss << "<"; + ss << TypeTagToString(*mTypeParams.begin()); + std::for_each(begin(mTypeParams) + 1, end(mTypeParams), [&ss](auto&& cur) { + ss << ", " << TypeTagToString(cur); + }); + ss << ">"; + } + return ss.str(); +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept { + stream << Bool::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept { + stream << U8::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept { + stream << U64::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept { + stream << U128::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept { + stream << TAddress::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept { + stream << TSigner::value; + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept { + auto res = st.serialize(); + stream.add_bytes(begin(res), end(res)); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& st) noexcept { + stream << TStructTag::value; + auto res = st.st.serialize(false); + stream.add_bytes(begin(res), end(res)); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept { + stream << Vector::value; + for (auto&& cur: t.tags) { + stream << cur; + } + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept { + std::visit([&stream](auto&& arg) { stream << arg; }, t.tags); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept { + stream << module.address() << module.name(); + return stream; +} +std::string TypeTagToString(const TypeTag& typeTag) noexcept { + auto visit_functor = [](const TypeTag::TypeTagVariant& value) -> std::string { + if (std::holds_alternative(value)) { + return "bool"; + } else if (std::holds_alternative(value)) { + return "u8"; + } else if (std::holds_alternative(value)) { + return "u64"; + } else if (std::holds_alternative(value)) { + return "u128"; + } else if (std::holds_alternative(value)) { + return "address"; + } else if (std::holds_alternative(value)) { + return "signer"; + } else if (auto* vectorData = std::get_if(&value); vectorData != nullptr && !vectorData->tags.empty()) { + std::stringstream ss; + ss << "vector<" << TypeTagToString(*vectorData->tags.begin()) << ">"; + return ss.str(); + } else if (auto* structData = std::get_if(&value); structData) { + return structData->string(); + } else if (auto* tStructData = std::get_if(&value); tStructData) { + return tStructData->st.string(); + } else { + return ""; + } + }; + + return std::visit(visit_functor, typeTag.tags); +} + +} // namespace TW::Aptos diff --git a/src/Aptos/MoveTypes.h b/src/Aptos/MoveTypes.h new file mode 100644 index 00000000000..d3810f300a3 --- /dev/null +++ b/src/Aptos/MoveTypes.h @@ -0,0 +1,101 @@ +// Copyright © 2017-2022 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 "Aptos/Address.h" +#include "BCS.h" +#include + +namespace TW::Aptos { + +constexpr std::uint8_t gCodeTag{0}; +constexpr std::uint8_t gResourceTag{1}; +using Identifier = std::string; + +class ModuleId { +public: + ///< Constructor + ModuleId(Address accountAddress, Identifier name) noexcept; + + ///< Getters + [[nodiscard]] const std::string& name() const noexcept { return mName; } + [[nodiscard]] const Address& address() const noexcept { return mAccountAddress; } + [[nodiscard]] Data accessVector() const noexcept; + [[nodiscard]] std::string string() const noexcept; + [[nodiscard]] std::string shortString() const noexcept; + +private: + Address mAccountAddress; + Identifier mName; +}; + +BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept; + +struct TypeTag; + +struct Bool { + static constexpr std::uint8_t value = 0; +}; +struct U8 { + static constexpr std::uint8_t value = 1; +}; +struct U64 { + static constexpr std::uint8_t value = 2; +}; +struct U128 { + static constexpr std::uint8_t value = 3; +}; +struct TAddress { + static constexpr std::uint8_t value = 4; +}; +struct TSigner { + static constexpr std::uint8_t value = 5; +}; +struct Vector { + static constexpr std::uint8_t value = 6; + std::vector tags; +}; + +class StructTag { +public: + explicit StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept; + [[nodiscard]] Data serialize(bool withResourceTag = true) const noexcept; + [[nodiscard]] ModuleId moduleID() const noexcept { return {mAccountAddress, mName}; }; + [[nodiscard]] std::string string() const noexcept; + +private: + Address mAccountAddress; + Identifier mModule; + Identifier mName; + std::vector mTypeParams; +}; + +// C++ limitation, the first StructTag will serialize with ResourceTag, the inner one will use the value 7 instead. Tweaking by wrapping the struct +struct TStructTag { + static constexpr std::uint8_t value = 7; + StructTag st; +}; + +struct TypeTag { + using TypeTagVariant = std::variant; + TypeTagVariant tags; +}; + +std::string TypeTagToString(const TypeTag& typeTag) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& t) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept; +static const TypeTag gTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(gAddressOne, "aptos_coin", "AptosCoin", {})})}; + +} // namespace TW::Aptos diff --git a/src/Aptos/Signer.cpp b/src/Aptos/Signer.cpp new file mode 100644 index 00000000000..eb1e05424d4 --- /dev/null +++ b/src/Aptos/Signer.cpp @@ -0,0 +1,115 @@ +// Copyright © 2017-2022 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 "Address.h" +#include "Hash.h" +#include "MoveTypes.h" +#include "TransactionBuilder.h" +#include "TransactionPayload.h" + +namespace TW::Aptos { + +template +std::pair, nlohmann::json> commonTransferPayload(const TPayload& input) { + BCS::Serializer aSerializer; + aSerializer << Address(input.to()); + std::vector args; + args.emplace_back(aSerializer.bytes); + aSerializer.clear(); + aSerializer << input.amount(); + args.emplace_back(aSerializer.bytes); + nlohmann::json argsJson = nlohmann::json::array({input.to(), std::to_string(input.amount())}); + return std::make_pair(args, argsJson); +} + +TransactionPayload transferPayload(const Proto::SigningInput& input) { + auto&& [args, argsJson] = commonTransferPayload(input.transfer()); + ModuleId module(gAddressOne, "coin"); + TransactionPayload payload = EntryFunction(module, "transfer", {gTransferTag}, args, argsJson); + return payload; +} + +TransactionPayload createAccountPayload(const Proto::SigningInput& input) { + ModuleId module(gAddressOne, "aptos_account"); + BCS::Serializer aSerializer; + aSerializer << Address(input.create_account().auth_key()); + std::vector args; + args.emplace_back(aSerializer.bytes); + nlohmann::json argsJson = nlohmann::json::array({input.create_account().auth_key()}); + TransactionPayload payload = EntryFunction(module, "create_account", {}, args, argsJson); + return payload; +} + +TransactionPayload tokenTransferPayload(const Proto::SigningInput& input) { + + auto&& [args, argsJson] = commonTransferPayload(input.token_transfer()); + auto& function = input.token_transfer().function(); + TypeTag tokenTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), + function.module(), function.name(), {})})}; + ModuleId module(gAddressOne, "coin"); + TransactionPayload payload = EntryFunction(module, "transfer", {tokenTransferTag}, args, argsJson); + return payload; +} + +Proto::SigningOutput blindSign(const Proto::SigningInput& input) { + auto output = Proto::SigningOutput(); + BCS::Serializer serializer; + auto encodedCall = parse_hex(input.any_encoded()); + serializer.add_bytes(begin(encodedCall), end(encodedCall)); + auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); + auto signature = privateKey.sign(encodedCall, TWCurveED25519); + auto pubKeyData = privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes; + output.set_raw_txn(encodedCall.data(), encodedCall.size()); + output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); + output.mutable_authenticator()->set_signature(signature.data(), signature.size()); + serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; + output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); + + // clang-format off + nlohmann::json json = { + {"type", "ed25519_signature"}, + {"public_key", hexEncoded(pubKeyData)}, + {"signature", hexEncoded(signature)} + }; + // clang-format on + + output.set_json(json.dump()); + return output; +} + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { + auto protoOutput = Proto::SigningOutput(); + if (!input.any_encoded().empty()) { + return blindSign(input); + } + auto payloadFunctor = [&input]() { + switch (input.transaction_payload_case()) { + case Proto::SigningInput::kTransfer: { + return transferPayload(input); + } + case Proto::SigningInput::kTokenTransfer: { + return tokenTransferPayload(input); + } + case Proto::SigningInput::kCreateAccount: + return createAccountPayload(input); + case Proto::SigningInput::TRANSACTION_PAYLOAD_NOT_SET: + throw std::runtime_error("Transaction payload should be set"); + } + }; + TransactionBuilder::builder() + .sender(Address(input.sender())) + .sequenceNumber(input.sequence_number()) + .payload(payloadFunctor()) + .maxGasAmount(input.max_gas_amount()) + .gasUnitPrice(input.gas_unit_price()) + .expirationTimestampSecs(input.expiration_timestamp_secs()) + .chainId(static_cast(input.chain_id())) + .sign(input, protoOutput); + return protoOutput; +} + +} // namespace TW::Aptos diff --git a/src/Aptos/Signer.h b/src/Aptos/Signer.h new file mode 100644 index 00000000000..ad1f09ffdd8 --- /dev/null +++ b/src/Aptos/Signer.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2022 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 "../PrivateKey.h" +#include "../proto/Aptos.pb.h" + +namespace TW::Aptos { + +inline const Data gAptosSalt = data("APTOS::RawTransaction"); + +/// Helper class that performs Aptos transaction signing. +class Signer { +public: + /// Hide default constructor + Signer() = delete; + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input); +}; + +} // namespace TW::Aptos diff --git a/src/Aptos/TransactionBuilder.h b/src/Aptos/TransactionBuilder.h new file mode 100644 index 00000000000..20f37a591ff --- /dev/null +++ b/src/Aptos/TransactionBuilder.h @@ -0,0 +1,100 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "TransactionPayload.h" +#include + +namespace TW::Aptos { + +class TransactionBuilder { +public: + TransactionBuilder() noexcept = default; + + static TransactionBuilder builder() noexcept { return {}; } + + TransactionBuilder& sender(Address sender) noexcept { + mSender = sender; + return *this; + } + + TransactionBuilder& sequenceNumber(std::uint64_t sequenceNumber) noexcept { + mSequenceNumber = sequenceNumber; + return *this; + } + + TransactionBuilder& payload(TransactionPayload payload) noexcept { + mPayload = std::move(payload); + return *this; + } + + TransactionBuilder& maxGasAmount(std::uint64_t maxGasAmount) noexcept { + mMaxGasAmount = maxGasAmount; + return *this; + } + + TransactionBuilder& gasUnitPrice(std::uint64_t gasUnitPrice) noexcept { + mGasUnitPrice = gasUnitPrice; + return *this; + } + + TransactionBuilder& expirationTimestampSecs(std::uint64_t expirationTimestampSecs) noexcept { + mExpirationTimestampSecs = expirationTimestampSecs; + return *this; + } + + TransactionBuilder& chainId(std::uint8_t chainId) noexcept { + mChainId = chainId; + return *this; + } + + TransactionBuilder& sign(const Proto::SigningInput& input, Proto::SigningOutput& output) noexcept { + BCS::Serializer serializer; + serializer << mSender << mSequenceNumber << mPayload << mMaxGasAmount << mGasUnitPrice << mExpirationTimestampSecs << mChainId; + auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); + output.set_raw_txn(serializer.bytes.data(), serializer.bytes.size()); + auto msgToSign = TW::Hash::sha3_256(gAptosSalt.data(), gAptosSalt.size()); + append(msgToSign, serializer.bytes); + auto signature = privateKey.sign(msgToSign, TWCurveED25519); + auto pubKeyData = privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes; + output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); + output.mutable_authenticator()->set_signature(signature.data(), signature.size()); + serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; + output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); + + // https://fullnode.devnet.aptoslabs.com/v1/spec#/operations/submit_transaction + // clang-format off + nlohmann::json json = { + {"sender", mSender.string()}, + {"sequence_number", std::to_string(mSequenceNumber)}, + {"max_gas_amount", std::to_string(mMaxGasAmount)}, + {"gas_unit_price", std::to_string(mGasUnitPrice)}, + {"expiration_timestamp_secs", std::to_string(mExpirationTimestampSecs)}, + {"payload", payloadToJson(mPayload)}, + {"signature", { + {"type", "ed25519_signature"}, + {"public_key", hexEncoded(pubKeyData)}, + {"signature", hexEncoded(signature)}} + } + }; + // clang-format on + output.set_json(json.dump()); + return *this; + } + +private: + Address mSender{}; + std::uint64_t mSequenceNumber{}; + TransactionPayload mPayload{}; + std::uint64_t mMaxGasAmount{}; + std::uint64_t mGasUnitPrice{}; + std::uint64_t mExpirationTimestampSecs{}; + std::uint8_t mChainId{}; +}; + +} // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.cpp b/src/Aptos/TransactionPayload.cpp new file mode 100644 index 00000000000..a37a6f7c60d --- /dev/null +++ b/src/Aptos/TransactionPayload.cpp @@ -0,0 +1,57 @@ +// Copyright © 2017-2022 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 +#include + +namespace TW::Aptos { + +EntryFunction::EntryFunction(ModuleId module, Identifier function, std::vector tyArgs, std::vector args, nlohmann::json jsonArgs) noexcept + : mModule(std::move(module)), mFunction(std::move(function)), mTyArgs(std::move(tyArgs)), mArgs(std::move(args)), mJsonArgs(std::move(jsonArgs)) { +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const EntryFunction& entryFunction) noexcept { + stream << entryFunction.module() << entryFunction.function() << entryFunction.tyArgs() << entryFunction.args(); + return stream; +} + +nlohmann::json payloadToJson(const TransactionPayload& payload) { + auto visit_functor = [](const TransactionPayload& value) -> nlohmann::json { + if (auto* entryFunction = std::get_if(&value); entryFunction) { + return entryFunction->json(); + } else { + return {}; + } + }; + + return std::visit(visit_functor, payload); +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, [[maybe_unused]] const Script& script) noexcept { + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, [[maybe_unused]] const ModuleBundle& moduleBundle) noexcept { + return stream; +} + +nlohmann::json EntryFunction::json() const noexcept { + nlohmann::json tyArgsJson = nlohmann::json::array(); + for (auto&& cur : mTyArgs) { + tyArgsJson.emplace_back(TypeTagToString(cur)); + } + // clang-format off + nlohmann::json out = { + {"type", "entry_function_payload"}, + {"function", mModule.shortString() + "::" + mFunction}, + {"type_arguments", tyArgsJson}, + {"arguments", mJsonArgs} + }; + // clang-format on + return out; +} + +} // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.h b/src/Aptos/TransactionPayload.h new file mode 100644 index 00000000000..e52124f84ff --- /dev/null +++ b/src/Aptos/TransactionPayload.h @@ -0,0 +1,46 @@ +// Copyright © 2017-2022 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 +#include +#include + +namespace TW::Aptos { + +/// Call a Move entry function. +class EntryFunction { +public: + explicit EntryFunction(ModuleId module, Identifier function, std::vector tyArgs, std::vector args, nlohmann::json jsonArgs = {}) noexcept; + [[nodiscard]] const ModuleId& module() const noexcept { return mModule; } + [[nodiscard]] const Identifier& function() const noexcept { return mFunction; } + [[nodiscard]] const std::vector& tyArgs() const noexcept { return mTyArgs; } + [[nodiscard]] const std::vector& args() const noexcept { return mArgs; } + [[nodiscard]] nlohmann::json json() const noexcept; + +private: + ModuleId mModule; + Identifier mFunction; + std::vector mTyArgs; + std::vector mArgs; + nlohmann::json mJsonArgs; +}; + + +class Script { +}; + +class ModuleBundle { +}; + +BCS::Serializer& operator<<(BCS::Serializer& stream, const EntryFunction& entryFunction) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const Script& script) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleBundle& moduleBundle) noexcept; +using TransactionPayload = std::variant; +nlohmann::json payloadToJson(const TransactionPayload& payload); + +} // namespace TW::Aptos diff --git a/src/BCS.cpp b/src/BCS.cpp new file mode 100644 index 00000000000..1959ba83079 --- /dev/null +++ b/src/BCS.cpp @@ -0,0 +1,42 @@ +// Copyright © 2017-2022 Trust Wallet. +// Created by Clément Doumergue + +// 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 "BCS.h" + +namespace TW::BCS { + +Serializer& operator<<(Serializer& stream, std::byte b) noexcept { + stream.add_byte(b); + return stream; +} + +Serializer& operator<<(Serializer& stream, uleb128 t) noexcept { + integral auto value = t.value; + + while (value >= 0x80) { + // Add the 7 lowest bits of data and set highest bit to 1 + stream << static_cast((value & 0x7f) | 0x80); + value >>= 7; + } + + // Add the remaining bits of data (highest bit is already 0 at this point) + stream << static_cast(value); + return stream; +} + +Serializer& operator<<(Serializer& stream, std::string_view sv) noexcept { + stream << uleb128{static_cast(sv.size())}; + stream.add_bytes(sv.begin(), sv.end()); + return stream; +} + +Serializer& operator<<(Serializer& stream, std::nullopt_t) noexcept { + stream << false; + return stream; +} + +} // namespace TW::BCS diff --git a/src/BCS.h b/src/BCS.h new file mode 100644 index 00000000000..5d795089649 --- /dev/null +++ b/src/BCS.h @@ -0,0 +1,222 @@ +// Copyright © 2017-2022 Trust Wallet. +// Created by Clément Doumergue + +// 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 +#include +#include +#include +#include +#include +#include + +#include "Data.h" +#include "concepts/tw_concepts.h" + +namespace TW::BCS { + +/// Implementation of BCS encoding (as specified by the Diem project, see github.com/diem/bcs#detailed-specifications) + +struct Serializer { + Data bytes; + + void add_byte(std::byte b) noexcept { + bytes.emplace_back(static_cast(b)); + } + + template + void add_bytes(Iterator first, Iterator last) noexcept { + std::transform(first, last, std::back_inserter(bytes), [](auto&& c) { + return static_cast(c); + }); + } + + void clear() noexcept { + bytes.clear(); + } +}; + +struct uleb128 { + uint32_t value; +}; + +namespace details { + +template +concept aggregate_struct = std::is_class_v> && std::is_aggregate_v>; + +template +concept map_container = requires(T t) { + typename T::key_type; + typename T::mapped_type; + { std::declval().size() } -> std::same_as; + }; + +template + requires integral || + floating_point || + std::same_as || + std::same_as || + std::same_as || + aggregate_struct || + map_container +struct is_serializable { + static constexpr auto value = true; +}; + +template +struct is_serializable> { + static constexpr auto value = is_serializable::value; +}; + +template +struct is_serializable> { + static constexpr auto value = (is_serializable::value && ...); +}; + +template +struct is_serializable> { + static constexpr auto value = is_serializable::value && is_serializable::value; +}; + +template +struct is_serializable> { + static constexpr auto value = is_serializable::value; +}; + +template +struct is_serializable> { + static constexpr auto value = (is_serializable::value && ...); +}; + +template +Serializer& serialize_integral_impl(Serializer& stream, T t, std::index_sequence) noexcept { + const char* bytes = reinterpret_cast(&t); + // Add each byte in little-endian order + return (stream << ... << static_cast(bytes[Is])); +} + +template +Serializer& serialize_tuple_impl(Serializer& stream, const T& t, std::index_sequence) noexcept { + return (stream << ... << std::get(t)); +} + +template +struct dependent_false { + static constexpr auto value = false; +}; + +template +constexpr auto to_tuple(T&& t) { + if constexpr (std::is_empty_v) { + return std::make_tuple(); + } else if constexpr (requires { [&t] { auto&& [a0] = t; }; }) { + auto&& [a0] = std::forward(t); + return std::make_tuple(a0); + } else if constexpr (requires { [&t] { auto&& [a0, a1] = t; }; }) { + auto&& [a0, a1] = std::forward(t); + return std::make_tuple(a0, a1); + } else if constexpr (requires { [&t] { auto&& [a0, a1, a2] = t; }; }) { + auto&& [a0, a1, a2] = std::forward(t); + return std::make_tuple(a0, a1, a2); + } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3] = t; }; }) { + auto&& [a0, a1, a2, a3] = std::forward(t); + return std::make_tuple(a0, a1, a2, a3); + } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3, a4] = t; }; }) { + auto&& [a0, a1, a2, a3, a4] = std::forward(t); + return std::make_tuple(a0, a1, a2, a3, a4); + } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3, a4, a5] = t; }; }) { + auto&& [a0, a1, a2, a3, a4, a5] = std::forward(t); + return std::make_tuple(a0, a1, a2, a3, a4, a5); + } else { + static_assert(dependent_false::value, "the structure has more than 6 members"); + } +} + +template +Serializer& serialize_struct_impl(Serializer& stream, const T& t) noexcept { + return stream << to_tuple(t); +} +} // namespace details + +template +concept PrimitiveSerializable = details::is_serializable::value; + +template +concept CustomSerializable = requires(T t) { + { std::declval() << t } -> std::same_as; + }; + +template +concept Serializable = PrimitiveSerializable || CustomSerializable; + +Serializer& operator<<(Serializer& stream, std::byte b) noexcept; + +template +Serializer& operator<<(Serializer& stream, T t) noexcept { + return details::serialize_integral_impl(stream, t, std::make_index_sequence{}); +} + +Serializer& operator<<(Serializer& stream, uleb128 t) noexcept; + +Serializer& operator<<(Serializer& stream, std::string_view sv) noexcept; + +template +Serializer& operator<<(Serializer& stream, const std::optional o) noexcept { + if (o.has_value()) { + stream << true; + stream << o.value(); + } else { + stream << false; + } + return stream; +} + +Serializer& operator<<(Serializer& stream, std::nullopt_t) noexcept; + +template +Serializer& operator<<(Serializer& stream, const std::tuple& t) noexcept { + return details::serialize_tuple_impl(stream, t, std::make_index_sequence{}); +} + +template +Serializer& operator<<(Serializer& stream, const std::pair& t) noexcept { + return details::serialize_tuple_impl(stream, t, std::make_index_sequence<2>{}); +} + +template +Serializer& operator<<(Serializer& stream, const T& t) noexcept { + return details::serialize_struct_impl(stream, t); +} + +template +Serializer& operator<<(Serializer& stream, const std::vector& t) noexcept { + stream << uleb128{static_cast(t.size())}; + for (auto&& cur: t) { + stream << cur; + } + return stream; +} + +template +Serializer& operator<<(Serializer& stream, const std::variant& t) noexcept { + stream << uleb128{static_cast(t.index())}; + std::visit([&stream](auto&& value) { stream << value; }, t); + return stream; +} + +template +Serializer& operator<<(Serializer& stream, const T& t) noexcept { + stream << uleb128{static_cast(t.size())}; + for (auto&& [k, v] : t) { + stream << std::make_tuple(k, v); + } + return stream; +} + +} // namespace TW::BCS diff --git a/src/Coin.cpp b/src/Coin.cpp index f22a8713fc1..b727c1488ed 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -16,6 +16,7 @@ #include "Aeternity/Entry.h" #include "Aion/Entry.h" #include "Algorand/Entry.h" +#include "Aptos/Entry.h" #include "Binance/Entry.h" #include "Bitcoin/Entry.h" #include "Cardano/Entry.h" @@ -63,6 +64,7 @@ using namespace std; Aeternity::Entry aeternityDP; Aion::Entry aionDP; Algorand::Entry algorandDP; +Aptos::Entry AptosDP; Binance::Entry binanceDP; Bitcoin::Entry bitcoinDP; Cardano::Entry cardanoDP; @@ -150,6 +152,7 @@ CoinEntry* coinDispatcher(TWCoinType coinType) { case TWBlockchainKusama: entry = &kusamaDP; break; case TWBlockchainNervos: entry = &NervosDP; break; case TWBlockchainEverscale: entry = &EverscaleDP; break; + case TWBlockchainAptos: entry = &AptosDP; break; // end_of_coin_dipatcher_switch_marker_do_not_modify default: entry = nullptr; break; diff --git a/src/concepts/tw_concepts.h b/src/concepts/tw_concepts.h new file mode 100644 index 00000000000..57e2455a078 --- /dev/null +++ b/src/concepts/tw_concepts.h @@ -0,0 +1,19 @@ +// Copyright © 2017-2022 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 + +namespace TW { + +template +concept integral = std::is_integral_v; + +template +concept floating_point = std::is_floating_point_v; + +} // namespace TW diff --git a/src/proto/Aptos.proto b/src/proto/Aptos.proto new file mode 100644 index 00000000000..70d4f4eb598 --- /dev/null +++ b/src/proto/Aptos.proto @@ -0,0 +1,92 @@ +// Copyright © 2017-2022 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. + +syntax = "proto3"; + +package TW.Aptos.Proto; +option java_package = "wallet.core.jni.proto"; + +// Necessary fields to process a TransferMessage +message TransferMessage { + // Destination Account address (string) + string to = 1; + // Amount to be transferred (uint64) + uint64 amount = 2; +} + +// Necessary tag for type function argument +message StructTag { + // Address of the account + string account_address = 1; + // Module name + string module = 2; + // Identifier + string name = 3; +} + +// Necessary fields to process a TokenTransferMessage +message TokenTransferMessage { + // Destination Account address (string) + string to = 1; + // Amount to be transferred (uint64) + uint64 amount = 2; + // token function to call, e.g BTC: 0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC + StructTag function = 3; +} + +// Necessary fields to process a CreateAccountMessage +message CreateAccountMessage { + // auth account address to create + string auth_key = 1; +} + +// Input data necessary to create a signed transaction. +message SigningInput { + // Sender Account address (string) + string sender = 1; + // Sequence number, incremented atomically for each tx present on the account, start at 0 (int64) + int64 sequence_number = 2; + oneof transaction_payload { + TransferMessage transfer = 3; + TokenTransferMessage token_transfer = 4; + CreateAccountMessage create_account = 5; + } + // Max gas amount that the user is willing to pay (uint64) + uint64 max_gas_amount = 6; + // Gas unit price - queried through API (uint64) + uint64 gas_unit_price = 7; + // Expiration timestamp for the transaction, can't be in the past (uint64) + uint64 expiration_timestamp_secs = 8; + // Chain id 1 (mainnet) 32(devnet) (uint32 - casted in uint8_t later) + uint32 chain_id = 9; + // Private key to sign the transaction (bytes) + bytes private_key = 10; + // hex encoded function to sign, use it for smart contract approval (string) + string any_encoded = 11; +} + +// Information related to the signed transaction +message TransactionAuthenticator { + // Signature part of the signed transaction (bytes) + bytes signature = 1; + // Public key of the signer (bytes) + bytes public_key = 2; +} + +// Transaction signing output. +message SigningOutput { + /// The raw transaction (bytes) + bytes raw_txn = 1; + + /// Public key and signature to authenticate + TransactionAuthenticator authenticator = 2; + + /// Signed and encoded transaction bytes. + bytes encoded = 3; + + // Transaction json format for api broadcasting (string) + string json = 4; +} diff --git a/swift/Tests/Blockchains/AptosTests.swift b/swift/Tests/Blockchains/AptosTests.swift new file mode 100644 index 00000000000..d617c67db98 --- /dev/null +++ b/swift/Tests/Blockchains/AptosTests.swift @@ -0,0 +1,48 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class AptosTests: XCTestCase { + func testAddress() { + let anyAddress = AnyAddress(string: "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108", coin: .aptos) + + XCTAssertEqual(anyAddress?.description, "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108") + XCTAssertEqual(anyAddress?.coin, .aptos) + + let invalid = "MQqpqMQgCBuiPkoXfgZZsJvuzCeI1zc00z6vHJj4" + XCTAssertNil(Data(hexString: invalid)) + XCTAssertNil(AnyAddress(string: invalid, coin: .aptos)) + XCTAssertFalse(AnyAddress.isValid(string: invalid, coin: .aptos)) + } + + func testSign() { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xbb3b3c33781c27e486afa2db854fb0a5c846d0967672feb2c6c3297a2b14e1ce?network=Devnet + let privateKeyData = Data(hexString: "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")! + let transferMsg = AptosTransferMessage.with { + $0.to = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + $0.amount = 1000 + } + let input = AptosSigningInput.with { + $0.chainID = 32 + $0.sender = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + $0.expirationTimestampSecs = 3664390082 + $0.gasUnitPrice = 100 + $0.maxGasAmount = 3296766 + $0.sequenceNumber = 15 + $0.transfer = transferMsg + $0.privateKey = privateKeyData + } + let output: AptosSigningOutput = AnySigner.sign(input: input, coin: .aptos) + let expectedRawTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000020" + let expectedSignature = "2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05" + let expectedSignedTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c402ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05" + XCTAssertEqual(output.rawTxn.hexString, expectedRawTx) + XCTAssertEqual(output.authenticator.signature.hexString, expectedSignature) + XCTAssertEqual(output.encoded.hexString, expectedSignedTx) + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 8fdf3190737..789a9a6627d 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -252,6 +252,9 @@ class CoinAddressDerivationTests: XCTestCase { case .everscale: let expectedResult = "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .aptos: + let expectedResult = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; + assertCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() } diff --git a/tests/Aptos/AddressTests.cpp b/tests/Aptos/AddressTests.cpp new file mode 100644 index 00000000000..92f156d077e --- /dev/null +++ b/tests/Aptos/AddressTests.cpp @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 Trust Wallet. +// Author: Clement Doumergue +// +// 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 "HexCoding.h" +#include "Aptos/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include +#include + +namespace TW::Aptos::tests { + +TEST(AptosAddress, Valid) { + ASSERT_TRUE(Address::isValid("0x1")); + ASSERT_TRUE(Address::isValid(gAddressOne.string())); + ASSERT_TRUE(Address::isValid(gAddressZero.string())); + ASSERT_TRUE(Address::isValid("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); + ASSERT_TRUE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); + ASSERT_TRUE(Address::isValid("19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c")); +} + +TEST(AptosAddress, Invalid) { + ASSERT_FALSE(Address::isValid("Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); // Invalid hex character + ASSERT_FALSE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb")); // Invalid length: too long + ASSERT_FALSE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175")); // Invalid length: too short +} + +TEST(AptosAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("088baa019f081d6eab8dff5c447f9ce2f83c1babf3d03686299eaf6a1e89156e")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(address.string(), "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); +} + +TEST(AptosAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("ad0e293a56c9fc648d1872a00521d97e6b65724519a2676c2c47cb95d131cf5a"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); +} + +TEST(AptosAddress, FromString) { + auto address = Address("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); + ASSERT_EQ(address.string(), "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); +} + +TEST(AptosAddress, ShortString) { + ASSERT_EQ(gAddressOne.string(), "0x0000000000000000000000000000000000000000000000000000000000000001"); + ASSERT_EQ(gAddressOne.shortString(), "1"); +} + +} // namespace TW::Aptos::tests diff --git a/tests/Aptos/MoveTypesTests.cpp b/tests/Aptos/MoveTypesTests.cpp new file mode 100644 index 00000000000..c2cffeafa9a --- /dev/null +++ b/tests/Aptos/MoveTypesTests.cpp @@ -0,0 +1,59 @@ +// Copyright © 2017-2022 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 +#include +#include + +namespace TW::Aptos::tests { + +TEST(AptosMoveTypes, ModuleId) { + ModuleId module(gAddressOne, "coin"); + ASSERT_EQ(module.address(), gAddressOne); + ASSERT_EQ(module.name(), "coin"); + ASSERT_EQ(hex(module.accessVector()), "00000000000000000000000000000000000000000000000000000000000000000104636f696e"); + ASSERT_EQ(module.string(), "0x0000000000000000000000000000000000000000000000000000000000000001::coin"); + ASSERT_EQ(module.shortString(), "0x1::coin"); +} + +TEST(AptosMoveTypes, StructTag) { + auto functorTest = [](T value, const std::string expectedHex) { + TypeTag t{.tags = value}; + StructTag st(gAddressOne, "abc", "abc", std::vector{{t}}); + ASSERT_EQ(st.moduleID().name(), "abc"); + ASSERT_EQ(st.moduleID().address(), gAddressOne); + ASSERT_EQ(hex(st.serialize()), expectedHex); + }; + functorTest(Bool{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630100"); + functorTest(U8{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630101"); + functorTest(U64{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630102"); + functorTest(U128{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630103"); + functorTest(TAddress{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630104"); + functorTest(TSigner{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630105"); + functorTest(Vector{.tags = std::vector{{TypeTag{.tags = U8{}}}}}, "0100000000000000000000000000000000000000000000000000000000000000010361626303616263010601"); + StructTag stInner(gAddressOne, "foo", "bar", std::vector{{U8{}}}); + functorTest(TStructTag{stInner}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630107000000000000000000000000000000000000000000000000000000000000000103666f6f036261720101"); +} + +TEST(AptosMoveTypes, TypeTagDisplay) { + auto functorTest = [](const TypeTag &value, const std::string& expected) { + ASSERT_EQ(TypeTagToString(value), expected); + }; + functorTest(TypeTag{.tags = Bool{}}, "bool"); + functorTest(TypeTag{.tags = U8{}}, "u8"); + functorTest(TypeTag{.tags = U64{}}, "u64"); + functorTest(TypeTag{.tags = U128{}}, "u128"); + functorTest(TypeTag{.tags = TAddress{}}, "address"); + functorTest(TypeTag{.tags = TSigner{}}, "signer"); + TypeTag t{.tags = TypeTag::TypeTagVariant(Vector{.tags = {{U8{}}}})}; + functorTest(t, "vector"); + StructTag st(gAddressOne, "foo", "bar", std::vector{{U8{}}}); + TypeTag anotherT{.tags = TypeTag::TypeTagVariant(st)}; + functorTest(anotherT, "0x1::foo::bar"); + functorTest(gTransferTag, "0x1::aptos_coin::AptosCoin"); +} + +} // namespace TW::Aptos::tests diff --git a/tests/Aptos/SignerTests.cpp b/tests/Aptos/SignerTests.cpp new file mode 100644 index 00000000000..ece577f4e0b --- /dev/null +++ b/tests/Aptos/SignerTests.cpp @@ -0,0 +1,231 @@ +// Copyright © 2017-2022 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 "Aptos/Address.h" +#include "Aptos/Signer.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include + +#include + +namespace TW::Aptos::tests { + +TEST(AptosSigner, DummyTxSign) { + Proto::SigningInput input; + input.set_sender("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); + input.set_sequence_number(1); + auto& tf = *input.mutable_transfer(); + tf.set_to("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); + tf.set_amount(1000); + input.set_max_gas_amount(1); + input.set_gas_unit_price(1); + input.set_expiration_timestamp_secs(1); + input.set_chain_id(1); + auto privateKey = PrivateKey(parse_hex("7f2634c0e2414a621e96e39c41d09021700cee12ee43328ed094c5580cd0bd6f")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b010000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e80300000000000001000000000000000100000000000000010000000000000001"); + ASSERT_EQ(hex(result.authenticator().signature()), "9d3bd902bd358364c43fa65ece335dd4411527e72e1c6deb9148744eaa24e39b6bd74ff6b0195114243bdd2ee3a98511ff05883d9e79161b2b8f5029d883c309"); + ASSERT_EQ(hex(result.encoded()), "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b010000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000010000000000000001000000000000000100000000000000010020633e5c7e355bdd484706436ce1f06fdf280bd7c2229a7f9b6489684412c6967c409d3bd902bd358364c43fa65ece335dd4411527e72e1c6deb9148744eaa24e39b6bd74ff6b0195114243bdd2ee3a98511ff05883d9e79161b2b8f5029d883c309"); + nlohmann::json expectedJson = R"( + { + "sender": "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + "sequence_number": "1", + "max_gas_amount": "1", + "gas_unit_price": "1", + "expiration_timestamp_secs": "1", + "payload": { + "type":"entry_function_payload", + "function": "0x1::coin::transfer", + "type_arguments":["0x1::aptos_coin::AptosCoin"], + "arguments": ["0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", "1000"] + }, + "signature": { + "type": "ed25519_signature", + "public_key": "0x633e5c7e355bdd484706436ce1f06fdf280bd7c2229a7f9b6489684412c6967c", + "signature": "0x9d3bd902bd358364c43fa65ece335dd4411527e72e1c6deb9148744eaa24e39b6bd74ff6b0195114243bdd2ee3a98511ff05883d9e79161b2b8f5029d883c309" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + ASSERT_EQ(expectedJson, parsedJson); +} + +TEST(AptosSigner, TxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xbb3b3c33781c27e486afa2db854fb0a5c846d0967672feb2c6c3297a2b14e1ce?network=Devnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(15); + auto& tf = *input.mutable_transfer(); + tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + tf.set_amount(1000); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(32); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000020"); + ASSERT_EQ(hex(result.authenticator().signature()), "2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c402ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "type_arguments": ["0x1::aptos_coin::AptosCoin"] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "15", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + ASSERT_EQ(expectedJson, parsedJson); +} + +TEST(AptosSigner, CreateAccount) { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x477141736de6b0936a6c3734e4d6fd018c7d21f1f28f99028ef0bc6881168602?network=Devnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(0); + auto& tf = *input.mutable_create_account(); + tf.set_auth_key("0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(33); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021"); + ASSERT_EQ(hex(result.authenticator().signature()), "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"], + "function": "0x1::aptos_account::create_account", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "0", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + ASSERT_EQ(expectedJson, parsedJson); +} + +TEST(AptosSigner, BlindSign) { + // successfully broadcasted https://explorer.aptoslabs.com/txn/0xd95857a9e644528708778a3a0a6e13986751944fca30eaac98853c1655de0422?network=Devnet + // encoded submission with: + // curl --location --request POST 'https://fullnode.devnet.aptoslabs.com/v1/transactions/encode_submission' \ + //--header 'Content-Type: application/json' \ + //--header 'Cookie: AWSALB=0zI2zWypvEr0I3sGM6vnyHSxYO1D0aaMXfyA/2VwhA291aJJ80Yz67Fur50sXPFBI8dKKID4p8DShj1KkEXPY/NGAylpOj1EG2M2Qjuu1B38Q5C+dZW2CHT+IAZ5; AWSALBCORS=0zI2zWypvEr0I3sGM6vnyHSxYO1D0aaMXfyA/2VwhA291aJJ80Yz67Fur50sXPFBI8dKKID4p8DShj1KkEXPY/NGAylpOj1EG2M2Qjuu1B38Q5C+dZW2CHT+IAZ5' \ + //--data-raw '{ + // "expiration_timestamp_secs": "3664390082", + // "gas_unit_price": "100", + // "max_gas_amount": "3296766", + // "payload": { + // "function": "0x4633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b298837::pool::swap_y_to_x", + // "type_arguments": [ + // "0xdeae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e01::devnet_coins::DevnetUSDT", + // "0x1::aptos_coin::AptosCoin" + // ], + // "arguments": [ + // "100000000", + // "0" + // ], + // "type": "entry_function_payload" + // }, + // "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + // "sequence_number": "2" + //}' + Proto::SigningInput input; + input.set_any_encoded("0xb5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + ASSERT_EQ(hex(result.authenticator().signature()), "9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); + ASSERT_EQ(hex(result.encoded()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c409e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); + nlohmann::json expectedJson = R"( + { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b", + "type": "ed25519_signature" + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + ASSERT_EQ(expectedJson, parsedJson); +} + +TEST(AptosSigner, TokenTxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb5b383a5c7f99b2edb3bed9533f8169a89051b149d65876a82f4c0b9bf78a15b?network=Devnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(24); + auto& tf = *input.mutable_token_transfer(); + tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + tf.set_amount(100000); + tf.mutable_function()->set_account_address("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9"); + tf.mutable_function()->set_module("coins"); + tf.mutable_function()->set_name("BTC"); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(32); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020"); + ASSERT_EQ(hex(result.authenticator().signature()), "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","100000"], + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "type_arguments": ["0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC"] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "24", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + ASSERT_EQ(expectedJson, parsedJson); +} + +} // namespace TW::Aptos::tests diff --git a/tests/Aptos/TWAnySignerTests.cpp b/tests/Aptos/TWAnySignerTests.cpp new file mode 100644 index 00000000000..1caf524cc5c --- /dev/null +++ b/tests/Aptos/TWAnySignerTests.cpp @@ -0,0 +1,63 @@ +// Copyright © 2017-2022 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 "Aptos/Address.h" +#include "Aptos/Signer.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include +#include "../interface/TWTestUtilities.h" + +#include + +namespace TW::Aptos::tests { + +TEST(TWAnySignerAptos, TxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xbb3b3c33781c27e486afa2db854fb0a5c846d0967672feb2c6c3297a2b14e1ce?network=Devnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(15); + auto& tf = *input.mutable_transfer(); + tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + tf.set_amount(1000); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(32); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeAptos); + ASSERT_EQ(hex(output.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000020"); + ASSERT_EQ(hex(output.authenticator().signature()), "2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05"); + ASSERT_EQ(hex(output.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c402ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "type_arguments": ["0x1::aptos_coin::AptosCoin"] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "15", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(output.json()); + ASSERT_EQ(expectedJson, parsedJson); +} + +} // namespace TW::Aptos::tests diff --git a/tests/Aptos/TWAptosAddressTests.cpp b/tests/Aptos/TWAptosAddressTests.cpp new file mode 100644 index 00000000000..cc827933612 --- /dev/null +++ b/tests/Aptos/TWAptosAddressTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "../interface/TWTestUtilities.h" +#include +#include + +#include + +using namespace TW; + +namespace TW::Aptos::tests { + +TEST(TWAptosAddress, HDWallet) { + auto mnemonic = + "shoot island position soft burden budget tooth cruel issue economy destroy above"; + auto passphrase = ""; + + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(STRING(mnemonic).get(), STRING(passphrase).get())); + + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), TWCoinTypeAptos, WRAPS(TWCoinTypeDerivationPath(TWCoinTypeAptos)).get())); + + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519(privateKey.get())); + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeAptos)); + auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); + + assertStringsEqual(addressStr, "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); +} + +} // namespace TW::Aptos::tests diff --git a/tests/Aptos/TWCoinTypeTests.cpp b/tests/Aptos/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..e9f26f59987 --- /dev/null +++ b/tests/Aptos/TWCoinTypeTests.cpp @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 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. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "../interface/TWTestUtilities.h" +#include +#include + + +TEST(TWAptosCoinType, TWCoinType) { + const auto coin = TWCoinTypeAptos; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("91424546")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "aptos"); + assertStringsEqual(name, "Aptos"); + assertStringsEqual(symbol, "APT"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 8); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainAptos); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(txUrl, "https://explorer.aptoslabs.com/txn/91424546"); + assertStringsEqual(accUrl, "https://explorer.aptoslabs.com/account/0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108"); +} diff --git a/tests/Aptos/TransactionPayloadTests.cpp b/tests/Aptos/TransactionPayloadTests.cpp new file mode 100644 index 00000000000..29fd5e3712a --- /dev/null +++ b/tests/Aptos/TransactionPayloadTests.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 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 +#include +#include + +namespace TW::Aptos::tests { + +TEST(AptosTransactionPayload, PayLoadBasis) { + ModuleId module(gAddressOne, "coin"); + std::uint64_t amount{1000}; + Address to("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); + BCS::Serializer serializer; + serializer << to; + std::vector args; + args.emplace_back(serializer.bytes); + serializer.clear(); + serializer << amount; + args.emplace_back(serializer.bytes); + TransactionPayload payload = EntryFunction(module, "transfer", {gTransferTag}, args); + ASSERT_EQ(std::get(payload).module().name(), "coin"); + ASSERT_EQ(std::get(payload).module().shortString(), "0x1::coin"); + serializer.clear(); + serializer << payload; + ASSERT_EQ(hex(serializer.bytes), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); +} + +} // namespace TW::Aptos::tests diff --git a/tests/BCSTests.cpp b/tests/BCSTests.cpp new file mode 100644 index 00000000000..1182d6e2d87 --- /dev/null +++ b/tests/BCSTests.cpp @@ -0,0 +1,165 @@ +// Copyright © 2017-2022 Trust Wallet. +// Created by Clément Doumergue +// +// 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 "BCS.h" +#include "HexCoding.h" + +#include + +namespace TW::BCS::tests { + +TEST(BCS, Integral) { + Serializer os; + os << uint32_t(0xAABBCCDD); + ASSERT_EQ(os.bytes, parse_hex("0xDDCCBBAA")); + + os.clear(); + os << int32_t(-305419896); + ASSERT_EQ(os.bytes, parse_hex("0x88A9CBED")); +} + +TEST(BCS, ULEB128) { + Serializer os; + os << uleb128{0x00000001}; + ASSERT_EQ(os.bytes, parse_hex("0x01")); + + os.clear(); + os << uleb128{0x00000080}; + ASSERT_EQ(os.bytes, parse_hex("0x8001")); + + os.clear(); + os << uleb128{0x00004000}; + ASSERT_EQ(os.bytes, parse_hex("0x808001")); + + os.clear(); + os << uleb128{0x00200000}; + ASSERT_EQ(os.bytes, parse_hex("0x80808001")); + + os.clear(); + os << uleb128{0x10000000}; + ASSERT_EQ(os.bytes, parse_hex("0x8080808001")); + + os.clear(); + os << uleb128{0x0000250F}; + ASSERT_EQ(os.bytes, parse_hex("0x8F4A")); +} + +TEST(BCS, String) { + Serializer os; + os << std::string_view("abcd"); + ASSERT_EQ(os.bytes, parse_hex("0x0461626364")); + + os.clear(); + os << std::string_view(""); + ASSERT_EQ(os.bytes, parse_hex("0x00")); +} + +TEST(BCS, Optional) { + Serializer os; + os << std::optional{0xBBCCDD}; + ASSERT_EQ(os.bytes, parse_hex("0x01DDCCBB00")); + + os.clear(); + os << std::optional{}; + ASSERT_EQ(os.bytes, parse_hex("0x00")); + + os.clear(); + os << std::nullopt; + ASSERT_EQ(os.bytes, parse_hex("0x00")); +} + +TEST(BCS, Tuple) { + Serializer os; + os << std::tuple{uint16_t(1), 'a'}; + ASSERT_EQ(os.bytes, parse_hex("0x010061")); + + os.clear(); + os << std::tuple{std::optional{123}, std::string_view("abcd"), uint8_t(0x0E)}; + ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); + + os.clear(); + os << std::tuple{}; + ASSERT_EQ(os.bytes, (Data{})); +} + +TEST(BCS, Pair) { + Serializer os; + os << std::pair{uint16_t(1), 'a'}; + ASSERT_EQ(os.bytes, parse_hex("0x010061")); + + os.clear(); + os << std::pair{std::optional{123}, std::string_view("abcd")}; + ASSERT_EQ(os.bytes, parse_hex("0x017b0000000461626364")); +} + +struct my_struct { + std::optional first; + std::string_view second; + uint8_t third; +}; + +TEST(BCS, Struct) { + Serializer os; + os << my_struct{{123}, "abcd", 0x0E}; + ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); +} + +TEST(BCS, Variant) { + using V = std::variant; + + Serializer os; + os << V{uint32_t(1)}; + ASSERT_EQ(os.bytes, parse_hex("0x0001000000")); + + os.clear(); + os << V{char('a')}; + ASSERT_EQ(os.bytes, parse_hex("0x0161")); + + os.clear(); + os << V{true}; + ASSERT_EQ(os.bytes, parse_hex("0x0201")); +} + +TEST(BCS, Map) { + Serializer os; + os << std::map{{'a', 0}, {'b', 1}, {'c', 2}}; + ASSERT_EQ(os.bytes, parse_hex("0x03610062016302")); +} + +class my_number { +private: + int value; + +public: + explicit my_number(int value) noexcept + : value(value) { + } + + [[nodiscard]] auto get_value() const { + return value; + } +}; + +Serializer& operator<<(Serializer& stream, my_number n) noexcept { + return stream << n.get_value(); +} + +static_assert(CustomSerializable, "my_number does not model the CustomSerializable concept"); + +TEST(BCS, Custom) { + Serializer os; + os << my_number{0xBBCCDD}; + ASSERT_EQ(os.bytes, parse_hex("0xDDCCBB00")); +} + +TEST(BCS, Vector) { + Serializer os; + os << std::vector{1}; + ASSERT_EQ(os.bytes, parse_hex("0101")); +} + +} diff --git a/tests/Cardano/TWCardanoAddressTests.cpp b/tests/Cardano/TWCardanoAddressTests.cpp index 4ce62f4e01a..e77f3e81520 100644 --- a/tests/Cardano/TWCardanoAddressTests.cpp +++ b/tests/Cardano/TWCardanoAddressTests.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "../interface/TWTestUtilities.h" #include "PrivateKey.h" diff --git a/tests/CoinAddressDerivationTests.cpp b/tests/CoinAddressDerivationTests.cpp index ed8d695148e..6d04e729eba 100644 --- a/tests/CoinAddressDerivationTests.cpp +++ b/tests/CoinAddressDerivationTests.cpp @@ -248,6 +248,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeNervos: EXPECT_EQ(address, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqtsqfsf77ae0wn5a7795hs2ydv83g6hl4qleywxw"); break; + case TWCoinTypeAptos: + EXPECT_EQ(address, "0xce2fd04ac9efa74f17595e5785e847a2399d7e637f5e8179244f76191f653276"); + break; // no default branch here, intentionally, to better notice any missing coins } } diff --git a/tests/HDWallet/HDWalletTests.cpp b/tests/HDWallet/HDWalletTests.cpp index fdf70a687d2..893eefb3a5f 100644 --- a/tests/HDWallet/HDWalletTests.cpp +++ b/tests/HDWallet/HDWalletTests.cpp @@ -423,4 +423,15 @@ TEST(HDWallet, getKey) { } } +TEST(HDWallet, AptosKey) { + const auto derivPath = "m/44'/637'/0'/0'/0'"; + HDWallet wallet = HDWallet(mnemonic1, ""); + { + const auto privateKey = wallet.getKey(TWCoinTypeAptos, DerivationPath(derivPath)); + EXPECT_EQ(hex(privateKey.bytes), "7f2634c0e2414a621e96e39c41d09021700cee12ee43328ed094c5580cd0bd6f"); + EXPECT_EQ(hex(privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes), "633e5c7e355bdd484706436ce1f06fdf280bd7c2229a7f9b6489684412c6967c"); + } +} + + } // namespace From 2bc44dabad0e2c2e7c582e0ac48d55866e650897 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Mon, 3 Oct 2022 13:35:15 +0200 Subject: [PATCH 005/426] [Comments] Add comments to Proto files (#2585) --- src/XRP/XAddress.h | 2 +- src/proto/Aeternity.proto | 7 +- src/proto/Aion.proto | 12 +- src/proto/Algorand.proto | 25 ++- src/proto/Binance.proto | 298 +++++++++++++++++++++++++-------- src/proto/Bitcoin.proto | 38 +++-- src/proto/Cardano.proto | 40 ++++- src/proto/Common.proto | 72 +++++--- src/proto/Cosmos.proto | 30 +++- src/proto/Decred.proto | 18 +- src/proto/EOS.proto | 13 +- src/proto/Elrond.proto | 41 ++++- src/proto/Ethereum.proto | 51 +++--- src/proto/Everscale.proto | 14 +- src/proto/FIO.proto | 23 ++- src/proto/Filecoin.proto | 11 +- src/proto/Harmony.proto | 66 ++++++-- src/proto/Icon.proto | 6 +- src/proto/IoTeX.proto | 267 +++++++++++++++++++---------- src/proto/NEAR.proto | 55 +++++- src/proto/NEO.proto | 37 +++- src/proto/NULS.proto | 73 +++++++- src/proto/Nano.proto | 9 +- src/proto/Nebulas.proto | 47 ++++-- src/proto/Nervos.proto | 10 +- src/proto/Nimiq.proto | 8 +- src/proto/Oasis.proto | 13 +- src/proto/Ontology.proto | 11 +- src/proto/Polkadot.proto | 53 +++++- src/proto/Ripple.proto | 12 +- src/proto/Solana.proto | 77 +++++++-- src/proto/Stellar.proto | 36 +++- src/proto/THORChainSwap.proto | 17 +- src/proto/Tezos.proto | 21 ++- src/proto/Theta.proto | 10 +- src/proto/Tron.proto | 57 ++++++- src/proto/VeChain.proto | 7 +- src/proto/Waves.proto | 31 +++- src/proto/Zilliqa.proto | 12 +- tests/FIO/TWFIOTests.cpp | 2 +- tools/doxygen_convert_comments | 2 +- 41 files changed, 1263 insertions(+), 371 deletions(-) diff --git a/src/XRP/XAddress.h b/src/XRP/XAddress.h index 75454734821..1d1e38a5271 100644 --- a/src/XRP/XAddress.h +++ b/src/XRP/XAddress.h @@ -20,7 +20,7 @@ class XAddress { /// Number of bytes in a X-address. static const size_t size = 31; - /// Publick key hash length. + /// Public key hash length. static const size_t keyHashSize = 20; /// Address data consisting of public key hash diff --git a/src/proto/Aeternity.proto b/src/proto/Aeternity.proto index 8a20764369a..fc501db4859 100644 --- a/src/proto/Aeternity.proto +++ b/src/proto/Aeternity.proto @@ -11,8 +11,10 @@ message SigningInput { // Address of the recipient with "ak_" prefix string to_address = 2; + // Amount (uint256, serialized little endian) bytes amount = 3; + // Fee amount (uint256, serialized little endian) bytes fee = 4; // Message, optional @@ -21,15 +23,18 @@ message SigningInput { // Time to live until block height uint64 ttl = 6; + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 7; + // The secret private key used for signing (32 bytes). bytes private_key = 8; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes, Base64 with checksum string encoded = 1; + // Signature, Base58 with checksum string signature = 2; } diff --git a/src/proto/Aion.proto b/src/proto/Aion.proto index 771e6774171..2b194fc0237 100644 --- a/src/proto/Aion.proto +++ b/src/proto/Aion.proto @@ -5,32 +5,32 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 1; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) bytes gas_price = 2; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 3; // Recipient's address. string to_address = 4; - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) bytes amount = 5; // Optional payload bytes payload = 6; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 7; // Timestamp uint64 timestamp = 8; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Algorand.proto b/src/proto/Algorand.proto index 937493e51a9..167fc0f10ee 100644 --- a/src/proto/Algorand.proto +++ b/src/proto/Algorand.proto @@ -3,18 +3,30 @@ syntax = "proto3"; package TW.Algorand.Proto; option java_package = "wallet.core.jni.proto"; +// Simple transfer message, transfer an amount to an address message Transfer { + // Destination address (string) string to_address = 1; + + // Amount uint64 amount = 2; } +// Asset Transfer message, with assetID message AssetTransfer { + // Destination address (string) string to_address = 1; + + // Amount uint64 amount = 2; + + // ID of the asset being transferred uint64 asset_id = 3; } +// Opt-in message for an asset message AssetOptIn { + // ID of the asset uint64 asset_id = 1; } @@ -22,19 +34,26 @@ message AssetOptIn { message SigningInput { // network / chain id string genesis_id = 1; + // network / chain hash bytes genesis_hash = 2; + // binary note data bytes note = 3; - // private key + + // The secret private key used for signing (32 bytes). bytes private_key = 4; + // network / first round uint64 first_round = 5; + // network / last round uint64 last_round = 6; - // fee + + // fee amount uint64 fee = 7; + // message payload oneof message_oneof { Transfer transfer = 10; AssetTransfer asset_transfer = 11; @@ -42,7 +61,7 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Binance.proto b/src/proto/Binance.proto index 375c1ec7586..00df9f21fd2 100644 --- a/src/proto/Binance.proto +++ b/src/proto/Binance.proto @@ -5,140 +5,258 @@ option java_package = "wallet.core.jni.proto"; import "Common.proto"; +// Transaction structure, used internally message Transaction { - // int64 SIZE-OF-ENCODED // varint encoded length of the structure after encoding - // 0xF0625DEE // prefix - repeated bytes msgs = 1; // array of size 1, containing the transaction message, which are one of the transaction type below - repeated bytes signatures = 2; // array of size 1, containing the standard signature structure of the transaction sender - string memo = 3; // a short sentence of remark for the transaction, only for `Transfer` transactions. - int64 source = 4; // an identifier for tools triggerring this transaction, set to zero if unwilling to disclose. - bytes data = 5; // reserved for future use + // array of size 1, containing the transaction message, which are one of the transaction type below + repeated bytes msgs = 1; + + // array of size 1, containing the standard signature structure of the transaction sender + repeated bytes signatures = 2; + + // a short sentence of remark for the transaction, only for `Transfer` transactions. + string memo = 3; + + // an identifier for tools triggering this transaction, set to zero if unwilling to disclose. + int64 source = 4; + + // reserved for future use + bytes data = 5; } +// Signature structure, used internally message Signature { - message PubKey { - // 0xEB5AE987 // prefix - // bytes // public key bytes - } - bytes pub_key = 1; // public key bytes of the signer address - bytes signature = 2; // signature bytes, please check chain access section for signature generation - int64 account_number = 3; // another identifier of signer, which can be read from chain by account REST API or RPC - int64 sequence = 4; // sequence number for the next transaction + // public key bytes of the signer address + bytes pub_key = 1; + + // signature bytes, please check chain access section for signature generation + bytes signature = 2; + + // another identifier of signer, which can be read from chain by account REST API or RPC + int64 account_number = 3; + + // sequence number for the next transaction + int64 sequence = 4; } +// Message for Trade order message TradeOrder { - // 0xCE6DC043 // prefix - bytes sender = 1; // originating address - string id = 2; // order id, optional - string symbol = 3; // symbol for trading pair in full name of the tokens - int64 ordertype = 4; // only accept 2 for now, meaning limit order - int64 side = 5; // 1 for buy and 2 fory sell - int64 price = 6; // price of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer - int64 quantity = 7; // quantity of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer - int64 timeinforce = 8; // 1 for Good Till Expire(GTE) order and 3 for Immediate Or Cancel (IOC) + // originating address + bytes sender = 1; + + // order id, optional + string id = 2; + + // symbol for trading pair in full name of the tokens + string symbol = 3; + + // only accept 2 for now, meaning limit order + int64 ordertype = 4; + + // 1 for buy and 2 for sell + int64 side = 5; + + // price of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer + int64 price = 6; + + // quantity of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer + int64 quantity = 7; + + // 1 for Good Till Expire(GTE) order and 3 for Immediate Or Cancel (IOC) + int64 timeinforce = 8; } +// Message for CancelTrade order message CancelTradeOrder { - // 0x166E681B // prefix - bytes sender = 1; // originating address - string symbol = 2; // symbol for trading pair in full name of the tokens - string refid = 3; // order id to cancel + // originating address + bytes sender = 1; + + // symbol for trading pair in full name of the tokens + string symbol = 2; + + // order id to cancel + string refid = 3; } +// Message for Send order message SendOrder { - // 0x2A2C87FA - // A symbol-amount pair. Could be moved out of SendOrder; kept here for backward compatibility. + // A token amount, symbol-amount pair. Could be moved out of SendOrder; kept here for backward compatibility. message Token { + // Token ID string denom = 1; + + // Amount int64 amount = 2; } + + // Transaction input message Input { + // source address bytes address = 1; + + // input coin amounts repeated Token coins = 2; } + + // Transaction output message Output { + // destination address bytes address = 1; + + // output coin amounts repeated Token coins = 2; } + + // Send inputs repeated Input inputs = 1; + + // Send outputs repeated Output outputs = 2; } +// Message for TokenIssue order message TokenIssueOrder { - // 0x17EFAB80 // prefix - bytes from = 1; // owner address - string name = 2; // token name - string symbol = 3; // token symbol, in full name with "-" suffix - int64 total_supply = 4; // total supply - bool mintable = 5; // mintable + // owner address + bytes from = 1; + + // token name + string name = 2; + + // token symbol, in full name with "-" suffix + string symbol = 3; + + // total supply + int64 total_supply = 4; + + // mintable + bool mintable = 5; } +// Message for TokenMint order message TokenMintOrder { - // 0x467E0829 // prefix - bytes from = 1; // owner address - string symbol = 2; // token symbol, in full name with "-" suffix - int64 amount = 3; // amount to mint + // owner address + bytes from = 1; + + // token symbol, in full name with "-" suffix + string symbol = 2; + + // amount to mint + int64 amount = 3; } +// Message for TokenBurn order message TokenBurnOrder { - // 0x7ED2D2A0 // prefix - bytes from = 1; // owner address - string symbol = 2; // token symbol, in full name with "-" suffix - int64 amount = 3; // amount to burn + // owner address + bytes from = 1; + + // token symbol, in full name with "-" suffix + string symbol = 2; + + // amount to burn + int64 amount = 3; } +// Message for TokenFreeze order message TokenFreezeOrder { - // 0xE774B32D // prefix - bytes from = 1; // owner address - string symbol = 2; // token symbol, in full name with "-" suffix - int64 amount = 3; // amount of token to freeze + // owner address + bytes from = 1; + + // token symbol, in full name with "-" suffix + string symbol = 2; + + // amount of token to freeze + int64 amount = 3; } +// Message for TokenUnfreeze order message TokenUnfreezeOrder { - // 0x6515FF0D // prefix - bytes from = 1; // owner address - string symbol = 2; // token symbol, in full name with "-" suffix - int64 amount = 3; // amount of token to unfreeze + // owner address + bytes from = 1; + + // token symbol, in full name with "-" suffix + string symbol = 2; + + // amount of token to unfreeze + int64 amount = 3; } +// Message for HashTimeLock order message HTLTOrder { - // 0xB33F9A24 // prefix - bytes from = 1; // signer address - bytes to = 2; // recipient address + // signer address + bytes from = 1; + + // recipient address + bytes to = 2; + + // source on other chain, optional string recipient_other_chain = 3; + + // recipient on other chain, optional string sender_other_chain = 4; - bytes random_number_hash = 5; //hash of a random number and timestamp, based on SHA256 + + // hash of a random number and timestamp, based on SHA256 + bytes random_number_hash = 5; + + // timestamp int64 timestamp = 6; + + // amounts repeated SendOrder.Token amount = 7; - string expected_income = 8; // expected gained token on the other chain + + // expected gained token on the other chain + string expected_income = 8; + + // period expressed in block heights int64 height_span = 9; + + // set for cross-chain send bool cross_chain = 10; } +// Message for Deposit HTLT order message DepositHTLTOrder { - // 0xB33F9A24 // prefix - bytes from = 1; // signer address + // signer address + bytes from = 1; + + // amounts repeated SendOrder.Token amount = 2; + + // swap ID bytes swap_id = 3; } +// Message for Claim HTLT order message ClaimHTLOrder { - // 0xC1665300 // prefix - bytes from = 1; // signer address + // signer address + bytes from = 1; + + // swap ID bytes swap_id = 2; + + // random number input bytes random_number = 3; } +// Message for Refund HTLT order message RefundHTLTOrder { - // 0x3454A27C // prefix - bytes from = 1; // signer address + // signer address + bytes from = 1; + + // swap ID bytes swap_id = 2; } +// Transfer message TransferOut { + // source address bytes from = 1; + + // recipient address bytes to = 2; + + // transfer amount SendOrder.Token amount = 3; + + // expiration time int64 expire_time = 4; } @@ -164,37 +282,73 @@ message SideChainUndelegate { string chain_id = 4; } +// Message for TimeLock order message TimeLockOrder { - bytes from_address = 1; // owner address + // owner address + bytes from_address = 1; + + // Description (optional) string description = 2; + // Array of symbol/amount pairs. see SDK https://github.com/binance-chain/javascript-sdk/blob/master/docs/api-docs/classes/tokenmanagement.md#timelock repeated SendOrder.Token amount = 3; + + // lock time int64 lock_time = 4; } +// Message for TimeRelock order message TimeRelockOrder { - bytes from_address = 1; // owner address - int64 id = 2; // order ID + // owner address + bytes from_address = 1; + + // order ID + int64 id = 2; + + // Description (optional) string description = 3; + // Array of symbol/amount pairs. repeated SendOrder.Token amount = 4; + + // lock time int64 lock_time = 5; } +// Message for TimeUnlock order message TimeUnlockOrder { - bytes from_address = 1; // owner address - int64 id = 2; // order ID + // owner address + bytes from_address = 1; + + // order ID + int64 id = 2; } -// Input data necessary to create a signed order. +// Input data necessary to create a signed transaction. message SigningInput { + // Chain ID string chain_id = 1; + + // Source account number int64 account_number = 2; + + // Sequence number (account specific) int64 sequence = 3; + + // Transaction source, see https://github.com/bnb-chain/BEPs/blob/master/BEP10.md + // Some important values: + // 0: Default source value (e.g. for Binance Chain Command Line, or SDKs) + // 1: Binance DEX Web Wallet + // 2: Trust Wallet int64 source = 4; + + // Optional memo string memo = 5; + + // The secret private key used for signing (32 bytes). bytes private_key = 6; + // Payload message oneof order_oneof { TradeOrder trade_order = 8; CancelTradeOrder cancel_trade_order = 9; @@ -218,14 +372,14 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; - /// error code, 0 is ok, other codes will be treated as errors + // OK (=0) or other codes in case of error Common.Proto.SigningError error = 2; - /// error description + // error description in case of error string error_message = 3; } diff --git a/src/proto/Bitcoin.proto b/src/proto/Bitcoin.proto index 1e9e631284a..cb321e44a8a 100644 --- a/src/proto/Bitcoin.proto +++ b/src/proto/Bitcoin.proto @@ -5,6 +5,7 @@ option java_package = "wallet.core.jni.proto"; import "Common.proto"; +// A transaction, with its inputs and outputs message Transaction { // Transaction data format version. sint32 version = 1; @@ -15,7 +16,7 @@ message Transaction { // A list of 1 or more transaction inputs or sources for coins. repeated TransactionInput inputs = 3; - // A list of 1 or more transaction outputs or destinations for coins + // A list of 1 or more transaction outputs or destinations for coins. repeated TransactionOutput outputs = 4; } @@ -33,7 +34,7 @@ message TransactionInput { // Bitcoin transaction out-point reference. message OutPoint { - // The hash of the referenced transaction. + // The hash of the referenced transaction (network byte order, usually needs to be reversed). bytes hash = 1; // The index of the specific output in the transaction. @@ -74,31 +75,31 @@ message SigningInput { // If amount is equal or more than the available amount, also max amount will be used. int64 amount = 2; - // Transaction fee per byte. + // Transaction fee rate, satoshis per byte, used to compute required fee (when planning) int64 byte_fee = 3; - // Recipient's address. + // Recipient's address, as string. string to_address = 4; - // Change address. + // Change address, as string. string change_address = 5; - // Available private keys. + // The available secret private key or keys required for signing (32 bytes each). repeated bytes private_key = 6; // Available redeem scripts indexed by script hash. map scripts = 7; - // Available unspent transaction outputs. + // Available input unspent transaction outputs. repeated UnspentTransaction utxo = 8; - // If sending max amount. + // Set if sending max amount is requested. bool use_max_amount = 9; - // Coin type (forks). + // Coin type (used by forks). uint32 coin_type = 10; - // Optional transaction plan + // Optional transaction plan. If missing, plan will be computed. TransactionPlan plan = 11; // Optional lockTime, default value 0 means no time locking. @@ -117,16 +118,16 @@ message TransactionPlan { // Amount to be received at the other end. int64 amount = 1; - // Maximum available amount. + // Maximum available amount in all the input UTXOs. int64 available_amount = 2; // Estimated transaction fee. int64 fee = 3; - // Change. + // Remaining change int64 change = 4; - // Selected unspent transaction outputs. + // Selected unspent transaction outputs (subset of all input UTXOs) repeated UnspentTransaction utxos = 5; // Zcash branch id @@ -139,23 +140,26 @@ message TransactionPlan { bytes output_op_return = 8; }; -// Transaction signing output. +// Result containing the signed and encoded transaction. +// Note that the amount may be different than the requested amount to account for fees and available funds. message SigningOutput { - // Resulting transaction. Note that the amount may be different than the requested amount to account for fees and available funds. + // Resulting transaction. Transaction transaction = 1; // Signed and encoded transaction bytes. bytes encoded = 2; - // Transaction id + // Transaction ID (hash) string transaction_id = 3; // Optional error Common.Proto.SigningError error = 4; + // error description string error_message = 5; } +/// Pre-image hash to be used for signing message HashPublicKey { /// Pre-image data hash that will be used for signing bytes data_hash = 1; @@ -174,4 +178,4 @@ message PreSigningOutput { /// error description string error_message = 3; -} \ No newline at end of file +} diff --git a/src/proto/Cardano.proto b/src/proto/Cardano.proto index c3ceaa27df9..ffeec086591 100644 --- a/src/proto/Cardano.proto +++ b/src/proto/Cardano.proto @@ -5,30 +5,45 @@ option java_package = "wallet.core.jni.proto"; import "Common.proto"; +// A transaction output that can be used as input message OutPoint { + // The transaction ID bytes tx_hash = 1; + + // The index of this output within the transaction uint64 output_index = 2; } +// Represents a token and an amount. Token is identified by PolicyID and name. message TokenAmount { - string policy_id = 1; // as hex string (28x2 digits) + // Policy ID of the token, as hex string (28x2 digits) + string policy_id = 1; + + // The name of the asset (within the policy) string asset_name = 2; - bytes amount = 3; // 256-bit number + + // The amount (uint256, serialized little endian) + bytes amount = 3; } +// One input for a transaction message TxInput { + // The UTXO OutPoint out_point = 1; + // The owner address (string) string address = 2; - // ADA amount + // ADA amount in the UTXO uint64 amount = 3; - // optional token amounts + // optional token amounts in the UTXO repeated TokenAmount token_amount = 4; } +// One output for a transaction message TxOutput { + // Destination address (string) string address = 1; // ADA amount @@ -43,6 +58,7 @@ message TokenBundle { repeated TokenAmount token = 1; } +// Message for simple Transfer tx message Transfer { // Destination address as string string to_address = 1; @@ -66,7 +82,7 @@ message Transfer { uint64 force_fee = 6; } -// Register a staking key for the account, preprequisite for Staking. +// Register a staking key for the account, prerequisite for Staking. // Note: staking messages are typically used with a 1-output-to-self transaction. message RegisterStakingKey { // Staking address (as string) @@ -106,6 +122,7 @@ message Withdraw { uint64 withdraw_amount = 2; } +// Describes a preliminary transaction plan. message TransactionPlan { // total coins in the utxos uint64 available_amount = 1; @@ -134,16 +151,19 @@ message TransactionPlan { // tokens in the change (optional) repeated TokenAmount change_tokens = 7; + // The selected UTXOs, subset ot the input UTXOs repeated TxInput utxos = 8; + // Optional error Common.Proto.SigningError error = 9; } -// Input data necessary to create a signed transaction +// Input data necessary to create a signed transaction. message SigningInput { + // Available input UTXOs repeated TxInput utxos = 1; - // Available private keys (double extended keys); every input UTXO adress should be covered + // Available private keys (double extended keys); every input UTXO address should be covered // In case of Plan only, keys should be present, in correct number repeated bytes private_key = 2; @@ -162,14 +182,16 @@ message SigningInput { // Optional DeregisterStakingKey deregister_staking_key = 9; + // Time-to-live time of the TX uint64 ttl = 4; - // Optional plan + // Optional plan, if missing it will be computed TransactionPlan plan = 5; } -// Transaction signing output +// Result containing the signed and encoded transaction. message SigningOutput { + // Encoded transaction bytes bytes encoded = 1; // TxID, derived from transaction data, also needed for submission diff --git a/src/proto/Common.proto b/src/proto/Common.proto index 7b24800829d..cedf89feee4 100644 --- a/src/proto/Common.proto +++ b/src/proto/Common.proto @@ -3,36 +3,68 @@ syntax = "proto3"; package TW.Common.Proto; option java_package = "wallet.core.jni.proto"; +// Error codes, used in multiple blockchains. enum SigningError { - OK = 0; // OK - // chain-generic, generic + // This is the OK case, with value=0 + OK = 0; + + // Chain-generic codes: + // Generic error (used if there is no suitable specific error is adequate) Error_general = 1; + // Internal error, indicates some very unusual, unexpected case Error_internal = 2; - // chain-generic, input + + // Chain-generic codes, input related: + // Low balance: the sender balance is not enough to cover the send and other auxiliary amount such as fee, deposit, or minimal balance. Error_low_balance = 3; - Error_zero_amount_requested = 4; // Requested amount is zero + // Requested amount is zero, send of 0 makes no sense + Error_zero_amount_requested = 4; + // One required key is missing (too few or wrong keys are provided) Error_missing_private_key = 5; + // A private key provided is invalid (e.g. wrong size, usually should be 32 bytes) Error_invalid_private_key = 15; + // A provided address (e.g. destination address) is invalid Error_invalid_address = 16; + // A provided input UTXO is invalid Error_invalid_utxo = 17; + // The amount of an input UTXO is invalid Error_invalid_utxo_amount = 18; - // chain-generic, fee + + // Chain-generic, fee related: + // Wrong fee is given, probably it is too low to cover minimal fee for the transaction Error_wrong_fee = 6; - // chain-generic, signing + + // Chain-generic, signing related: + // General signing error Error_signing = 7; - Error_tx_too_big = 8; // [NEO] Transaction too big, fee in GAS needed or try send by parts - // UTXO-chain specific, inputs - Error_missing_input_utxos = 9; // No UTXOs provided [BTC] - Error_not_enough_utxos = 10; // Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out) [BTC] - // UTXO-chain specific, script - Error_script_redeem = 11; // [BTC] Missing redeem script - Error_script_output = 12; // [BTC] Invalid output script - Error_script_witness_program = 13; // [BTC] Unrecognized witness program - - Error_invalid_memo = 14; // e.g. [XRP] Invalid tag - Error_input_parse = 19; // e.g. Invalid input data - Error_no_support_n2n = 20; // e.g. Not support multi-input and multi-output transaction - Error_signatures_count = 21; // Incorrect count of signatures passed to compile - Error_invalid_params = 22; // Incorrect parameters + // Resulting transaction is too large + // [NEO] Transaction too big, fee in GAS needed or try send by parts + Error_tx_too_big = 8; + + // UTXO-chain specific, input related: + // No input UTXOs provided [BTC] + Error_missing_input_utxos = 9; + // Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out) [BTC] + Error_not_enough_utxos = 10; + + // UTXO-chain specific, script related: + // [BTC] Missing required redeem script + Error_script_redeem = 11; + // [BTC] Invalid required output script + Error_script_output = 12; + // [BTC] Unrecognized witness program + Error_script_witness_program = 13; + + // Invalid memo, e.g. [XRP] Invalid tag + Error_invalid_memo = 14; + // Some input field cannot be parsed + Error_input_parse = 19; + // Multi-input and multi-output transaction not supported + Error_no_support_n2n = 20; + // Incorrect count of signatures passed to compile + Error_signatures_count = 21; + // Incorrect input parameter + Error_invalid_params = 22; + // Invalid input token amount Error_invalid_requested_token_amount = 23; } diff --git a/src/proto/Cosmos.proto b/src/proto/Cosmos.proto index ddeacaa1f5a..e95b00daa37 100644 --- a/src/proto/Cosmos.proto +++ b/src/proto/Cosmos.proto @@ -3,13 +3,21 @@ syntax = "proto3"; package TW.Cosmos.Proto; option java_package = "wallet.core.jni.proto"; +// A denomination and an amount message Amount { + // name of the denomination string denom = 1; + + // amount, number as string string amount = 2; } +// Fee incl. gas message Fee { + // Fee amount(s) repeated Amount amounts = 1; + + // Gas price uint64 gas = 2; } @@ -22,12 +30,14 @@ message Height { uint64 revision_height = 2; } +// Transaction broadcast mode enum BroadcastMode { BLOCK = 0; // Wait for the tx to pass/fail CheckTx, DeliverTx, and be committed in a block SYNC = 1; // Wait for the tx to pass/fail CheckTx ASYNC = 2; // Don't wait for pass/fail CheckTx; send and return tx immediately } +// A transaction payload message message Message { // cosmos-sdk/MsgSend message Send { @@ -260,6 +270,7 @@ message Message { string msg_type_url = 3; } + // The payload message oneof message_oneof { Send send_coins_message = 1; Transfer transfer_tokens_message = 2; @@ -281,30 +292,43 @@ message Message { } } +// Options for transaction encoding: JSON (Amino, older) or Protobuf. enum SigningMode { JSON = 0; // JSON format, Pre-Stargate Protobuf = 1; // Protobuf-serialized (binary), Stargate } -// Input data necessary to create a signed order. +// Input data necessary to create a signed transaction. message SigningInput { - // Specify if Stargate or earlier serialization is used + // Specify if protobuf (a.k.a. Stargate) or earlier JSON serialization is used SigningMode signing_mode = 1; + // Source account number uint64 account_number = 2; + + // Chain ID (string) string chain_id = 3; + + // Transaction fee Fee fee = 4; + + // Optional memo string memo = 5; + + // Sequence number (account specific) uint64 sequence = 6; + // The secret private key used for signing (32 bytes). bytes private_key = 7; + // Payload message(s) repeated Message messages = 8; + // Broadcast mode (included in output, relevant when broadcasting) BroadcastMode mode = 9; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signature bytes signature = 1; diff --git a/src/proto/Decred.proto b/src/proto/Decred.proto index d8355f5e6b5..fe9a8ff5663 100644 --- a/src/proto/Decred.proto +++ b/src/proto/Decred.proto @@ -6,11 +6,12 @@ option java_package = "wallet.core.jni.proto"; import "Bitcoin.proto"; import "Common.proto"; +// A transfer transaction message Transaction { - /// Serialization format + // Serialization format uint32 serializeType = 1; - /// Transaction data format version + // Transaction data format version uint32 version = 2; // A list of 1 or more transaction inputs or sources for coins. @@ -19,10 +20,10 @@ message Transaction { // A list of 1 or more transaction outputs or destinations for coins repeated TransactionOutput outputs = 4; - /// The time when a transaction can be spent (usually zero, in which case it has no effect). + // The time when a transaction can be spent (usually zero, in which case it has no effect). uint32 lockTime = 5; - /// The block height at which the transaction expires and is no longer valid. + // The block height at which the transaction expires and is no longer valid. uint32 expiry = 6; } @@ -34,8 +35,13 @@ message TransactionInput { // Transaction version as defined by the sender. uint32 sequence = 2; + // The amount of the input int64 valueIn = 3; + + // Creation block height uint32 blockHeight = 4; + + // Index within the block uint32 blockIndex = 5; // Computational script for confirming transaction authorization. @@ -47,14 +53,14 @@ message TransactionOutput { // Transaction amount. int64 value = 1; - /// Transaction output version. + // Transaction output version. uint32 version = 2; // Usually contains the public key as a Decred script setting up conditions to claim this output. bytes script = 3; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Resulting transaction. Note that the amount may be different than the requested amount to account for fees and available funds. Transaction transaction = 1; diff --git a/src/proto/EOS.proto b/src/proto/EOS.proto index d6a09f94f41..7f32c0a75fd 100644 --- a/src/proto/EOS.proto +++ b/src/proto/EOS.proto @@ -13,17 +13,22 @@ enum KeyType { // Values for an Asset object. message Asset { + // Total amount int64 amount = 1; + + // Number of decimals defined uint32 decimals = 2; + + // Asset symbol string symbol = 3; } // Input data necessary to create a signed transaction. message SigningInput { - // Chain id (256-bit number) + // Chain id (uint256, serialized little endian) bytes chain_id = 1; - // Reference Block Id (256-bits) + // Reference Block Id (uint256, serialized little endian) bytes reference_block_id = 2; // Timestamp on the reference block @@ -44,14 +49,14 @@ message SigningInput { // Asset details and amount Asset asset = 8; - // Sender's private key's raw bytes + // Sender's secret private key used for signing (32 bytes). bytes private_key = 9; // Type of the private key KeyType private_key_type = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // JSON of the packed transaction. string json_encoded = 1; diff --git a/src/proto/Elrond.proto b/src/proto/Elrond.proto index a2f4c59728a..9951055fde1 100644 --- a/src/proto/Elrond.proto +++ b/src/proto/Elrond.proto @@ -11,10 +11,18 @@ option java_package = "wallet.core.jni.proto"; // Generic action. Using one of the more specific actions (e.g. transfers, see below) is recommended. message GenericAction { + // Accounts involved Accounts accounts = 1; + + // amount string value = 2; + + // additional data string data = 3; + + // transaction version uint32 version = 4; + // Currently, the "options" field should be ignored (not set) by applications using TW Core. // In the future, TW Core will handle specific transaction options // (such as the "SignedWithHash" flag, as seen in https://github.com/ElrondNetwork/elrond-go-core/blob/main/core/versioning/txVersionChecker.go) @@ -24,41 +32,72 @@ message GenericAction { // EGLD transfer (move balance). message EGLDTransfer { + // Accounts involved Accounts accounts = 1; + + // Transfer amount (string) string amount = 2; } // ESDT transfer (transfer regular ESDTs - fungible tokens). message ESDTTransfer { + // Accounts involved Accounts accounts = 1; + + // Token ID string token_identifier = 2; + + // Transfer token amount (string) string amount = 3; } // ESDTNFT transfer (transfer NFTs, SFTs and Meta ESDTs). message ESDTNFTTransfer { + // Accounts involved Accounts accounts = 1; + + // tokens string token_collection = 2; + + // nonce of the token uint64 token_nonce = 3; + + // transfer amount string amount = 4; } // Transaction sender & receiver etc. message Accounts { + // Nonce of the sender uint64 sender_nonce = 1; + + // Sender address string sender = 2; + + // Sender username string sender_username = 3; + + // Receiver address string receiver = 4; + string receiver_username = 5; } // Input data necessary to create a signed transaction. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + + // Chain identifier, string string chain_id = 2; + + // Gas price uint64 gas_price = 3; + + // Limit for the gas used uint64 gas_limit = 4; + // transfer payload oneof message_oneof { GenericAction generic_action = 5; EGLDTransfer egld_transfer = 6; @@ -67,7 +106,7 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { string encoded = 1; string signature = 2; diff --git a/src/proto/Ethereum.proto b/src/proto/Ethereum.proto index f67ea7b4100..ce3ab148556 100644 --- a/src/proto/Ethereum.proto +++ b/src/proto/Ethereum.proto @@ -9,7 +9,7 @@ import "Common.proto"; message Transaction { // Native coin transfer transaction message Transfer { - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) bytes amount = 1; // Optional payload data @@ -18,40 +18,46 @@ message Transaction { // ERC20 token transfer transaction message ERC20Transfer { + // destination address (string) string to = 1; - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) bytes amount = 2; } // ERC20 approve transaction message ERC20Approve { + // Target of the approval string spender = 1; - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) bytes amount = 2; } // ERC721 NFT transfer transaction message ERC721Transfer { + // Source address string from = 1; + // Destination address string to = 2; - // ID of the token (256-bit number) + // ID of the token (uint256, serialized little endian) bytes token_id = 3; } // ERC1155 NFT transfer transaction message ERC1155Transfer { + // Source address string from = 1; + // Destination address string to = 2; - // ID of the token (256-bit number) + // ID of the token (uint256, serialized little endian) bytes token_id = 3; - // The amount of tokens being transferred + // The amount of tokens being transferred (uint256, serialized little endian) bytes value = 4; bytes data = 5; @@ -59,13 +65,14 @@ message Transaction { // Generic smart contract transaction message ContractGeneric { - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) bytes amount = 1; // Contract call payload data bytes data = 2; } + // Payload transfer oneof transaction_oneof { Transfer transfer = 1; ERC20Transfer erc20_transfer = 2; @@ -76,53 +83,59 @@ message Transaction { } } +// Transaction type enum TransactionMode { - Legacy = 0; // Legacy transaction, pre-EIP2718/EIP1559; for fee gasPrice/gasLimit is used - Enveloped = 1; // Enveloped transaction EIP2718 (with type 0x2), fee is according to EIP1559 (base fee, inclusion fee, ...) + // Legacy transaction, pre-EIP2718/EIP1559; for fee gasPrice/gasLimit is used + Legacy = 0; + + // Enveloped transaction EIP2718 (with type 0x2), fee is according to EIP1559 (base fee, inclusion fee, ...) + Enveloped = 1; } // Input data necessary to create a signed transaction. // Legacy and EIP2718/EIP1559 transactions supported, see TransactionMode. message SigningInput { - // Chain identifier (256-bit number) + // Chain identifier (uint256, serialized little endian) bytes chain_id = 1; - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 2; // Transaction version selector: Legacy or enveloped, has impact on fee structure. // Default is Legacy (value 0) TransactionMode tx_mode = 3; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) // Relevant for legacy transactions only (disregarded for enveloped/EIP1559) bytes gas_price = 4; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 5; - // Maxinmum optional inclusion fee (aka tip) (256-bit number) + // Maximum optional inclusion fee (aka tip) (uint256, serialized little endian) // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) bytes max_inclusion_fee_per_gas = 6; - // Maxinmum fee (256-bit number) + // Maximum fee (uint256, serialized little endian) // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) bytes max_fee_per_gas = 7; // Recipient's address. string to_address = 8; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 9; + // The payload transaction Transaction transaction = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; + // The V, R, S components of the resulting signature, (each uint256, serialized little endian) bytes v = 2; bytes r = 3; bytes s = 4; @@ -130,9 +143,9 @@ message SigningOutput { // The payload part, supplied in the input or assembled from input parameters bytes data = 5; - /// error code, 0 is ok, other codes will be treated as errors + // error code, 0 is ok, other codes will be treated as errors Common.Proto.SigningError error = 6; - /// error code description + // error code description string error_message = 7; } diff --git a/src/proto/Everscale.proto b/src/proto/Everscale.proto index c98841981c4..54ed4bb825c 100644 --- a/src/proto/Everscale.proto +++ b/src/proto/Everscale.proto @@ -9,11 +9,17 @@ syntax = "proto3"; package TW.Everscale.Proto; option java_package = "wallet.core.jni.proto"; + +// Message option enum MessageBehavior { - SimpleTransfer = 0; // Sends a message with the specified amount. The sender pays a fee from the account balance - SendAllBalance = 1; // Sends the entire account balance along with the message + // Sends a message with the specified amount. The sender pays a fee from the account balance + SimpleTransfer = 0; + + // Sends the entire account balance along with the message + SendAllBalance = 1; } +// Transfer message message Transfer { // If set to true, then the message will be returned if there is an error on the recipient's side. bool bounce = 1; @@ -37,14 +43,18 @@ message Transfer { } } +// Input data necessary to create a signed transaction. message SigningInput { + // The payload transfer oneof action_oneof { Transfer transfer = 1; } + // The secret private key used for signing (32 bytes). bytes private_key = 2; } +// Result containing the signed and encoded transaction. message SigningOutput { string encoded = 1; } diff --git a/src/proto/FIO.proto b/src/proto/FIO.proto index 5579cde39dd..e946dcf3fa7 100644 --- a/src/proto/FIO.proto +++ b/src/proto/FIO.proto @@ -52,6 +52,7 @@ message Action { } // Acion for adding public chain addresses to a FIO name; add_pub_address + // Note: actor is not needed, computed from private key message AddPubAddress { // The FIO name already registered to the owner. Ex.: "alice@trust" string fio_address = 1; @@ -60,12 +61,11 @@ message Action { repeated PublicAddress public_addresses = 2; // Max fee to spend, can be obtained using get_fee API. - uint64 fee = 3; - - // Note: actor is not needed, computed from private key + uint64 fee = 3; } - // Action for transfering FIO coins; transfer_tokens_pub_key + // Action for transferring FIO coins; transfer_tokens_pub_key + // Note: actor is not needed, computed from private key message Transfer { // FIO address of the payee. Ex.: "FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf" string payee_public_key = 1; @@ -75,11 +75,10 @@ message Action { // Max fee to spend, can be obtained using get_fee API. uint64 fee = 3; - - // Note: actor is not needed, computed from private key } // Action for renewing a FIO name; renew_fio_address + // Note: actor is not needed, computed from private key message RenewFioAddress { // The FIO name to be renewed. Ex.: "alice@trust" string fio_address = 1; @@ -89,11 +88,10 @@ message Action { // Max fee to spend, can be obtained using get_fee API. uint64 fee = 3; - - // Note: actor is not needed, computed from owner_fio_public_key } // Action for creating a new payment request; new_funds_request + // Note: actor is not needed, computed from private key message NewFundsRequest { // The FIO name of the requested payer. Ex.: "alice@trust" string payer_fio_name = 1; @@ -109,10 +107,9 @@ message Action { // Max fee to spend, can be obtained using get_fee API. uint64 fee = 5; - - // Note: actor is not needed, computed from private key } + // Payload message oneof message_oneof { RegisterFioAddress register_fio_address_message = 1; AddPubAddress add_pub_address_message = 2; @@ -134,7 +131,7 @@ message ChainParams { uint64 ref_block_prefix = 3; } -// Transaction signing input +// Input data necessary to create a signed transaction. message SigningInput { // Expiry for this message, in unix time. Can be 0, then it is taken from current time with default expiry uint32 expiry = 1; @@ -142,7 +139,7 @@ message SigningInput { // Current parameters of the FIO blockchain ChainParams chain_params = 2; - // The private key matching the address, needed for signing + // The secret private key matching the address, used for signing (32 bytes). bytes private_key = 3; // The FIO name of the originating wallet (project-wide constant) @@ -152,7 +149,7 @@ message SigningInput { Action action = 5; } -// Transaction signing output +// Result containing the signed and encoded transaction. message SigningOutput { // Signed transaction in JSON string json = 1; diff --git a/src/proto/Filecoin.proto b/src/proto/Filecoin.proto index 0c39da82c72..58ef6a156c0 100644 --- a/src/proto/Filecoin.proto +++ b/src/proto/Filecoin.proto @@ -5,7 +5,7 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Private key of sender account. + // The secret private key of the sender account, used for signing (32 bytes). bytes private_key = 1; // Recipient's address. @@ -14,20 +14,21 @@ message SigningInput { // Transaction nonce. uint64 nonce = 3; - // Transfer value. + // Transfer value (uint256, serialized little endian) bytes value = 4; // Gas limit. int64 gas_limit = 5; - // Gas fee cap. + // Gas fee cap (uint256, serialized little endian) bytes gas_fee_cap = 6; - // Gas premium. + // Gas premium (uint256, serialized little endian) bytes gas_premium = 7; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // Resulting transaction, in JSON. string json = 1; } diff --git a/src/proto/Harmony.proto b/src/proto/Harmony.proto index 72762c24acd..86647fad3bd 100644 --- a/src/proto/Harmony.proto +++ b/src/proto/Harmony.proto @@ -5,54 +5,58 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Chain identifier (256-bit number) + // Chain identifier (uint256, serialized little endian) bytes chain_id = 1; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 2; + // The payload message oneof message_oneof { TransactionMessage transaction_message = 3; StakingMessage staking_message = 4; } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; + // THE V,R,S components of the signature bytes v = 2; bytes r = 3; bytes s = 4; } +// A Transfer message message TransactionMessage { - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 1; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) bytes gas_price = 2; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 3; // Recipient's address. string to_address = 4; - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) bytes amount = 5; // Optional payload bytes payload = 6; - // From shard ID (256-bit number) + // From shard ID (uint256, serialized little endian) bytes from_shard_id = 7; - // To Shard ID (256-bit number) + // To Shard ID (uint256, serialized little endian) bytes to_shard_id = 8; } +// A Staking message. message StakingMessage { // StakeMsg oneof stake_msg { @@ -63,16 +67,17 @@ message StakingMessage { DirectiveCollectRewards collect_rewards = 5; } - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 6; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) bytes gas_price = 7; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 8; } +// Description for a validator message Description { string name = 1; string identity = 2; @@ -81,21 +86,38 @@ message Description { string details = 5; } +// A variable precision number message Decimal { + // The 'raw' value bytes value = 1; + + // The precision (number of decimals) bytes precision = 2; } +// Represents validator commission rule message CommissionRate { + // The rate Decimal rate = 1; + + // Maximum rate Decimal max_rate = 2; + + // Maximum of rate change Decimal max_change_rate = 3; } +// Create Validator directive message DirectiveCreateValidator { + // Address of validator string validator_address = 1; + + // Description, name etc. Description description = 2; + + // Rates CommissionRate commission_rates = 3; + bytes min_self_delegation = 4; bytes max_total_delegation = 5; repeated bytes slot_pub_keys = 6; @@ -103,7 +125,10 @@ message DirectiveCreateValidator { bytes amount = 8; } + +// Edit Validator directive message DirectiveEditValidator { + // Validator address string validator_address = 1; Description description = 2; Decimal commission_rate = 3; @@ -115,18 +140,33 @@ message DirectiveEditValidator { bytes active = 9; } +// Delegate directive message DirectiveDelegate { + // Delegator address string delegator_address = 1; + + // Validator address string validator_address = 2; + + // Delegate amount (uint256, serialized little endian) bytes amount = 3; } +// Undelegate directive message DirectiveUndelegate { + // Delegator address string delegator_address = 1; + + // Validator address string validator_address = 2; + + // Undelegate amount (uint256, serialized little endian) bytes amount = 3; } + +// Collect reward message DirectiveCollectRewards { + // Delegator address string delegator_address = 1; -} \ No newline at end of file +} diff --git a/src/proto/Icon.proto b/src/proto/Icon.proto index 8319b026d7c..fc39efc3ee8 100644 --- a/src/proto/Icon.proto +++ b/src/proto/Icon.proto @@ -11,7 +11,7 @@ message SigningInput { // Recipient address. string to_address = 2; - // Transfer amount. + // Transfer amount (uint256, serialized little endian) bytes value = 3; // The amount of step to send with the transaction. @@ -26,11 +26,11 @@ message SigningInput { // Network identifier bytes network_id = 7; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 8; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // JSON-encoded transaction parameters. string encoded = 1; diff --git a/src/proto/IoTeX.proto b/src/proto/IoTeX.proto index 1df337cf627..275dd40d8ab 100644 --- a/src/proto/IoTeX.proto +++ b/src/proto/IoTeX.proto @@ -3,96 +3,159 @@ syntax = "proto3"; package TW.IoTeX.Proto; option java_package = "wallet.core.jni.proto"; +// A transfer message Transfer { + // Amount (as string) string amount = 1; + + // Destination address string recipient = 2; + + // Payload data bytes payload = 3; } +// A Staking message message Staking { - // create stake - message Create { - string candidateName = 1; - string stakedAmount = 2; - uint32 stakedDuration = 3; - bool autoStake = 4; - bytes payload = 5; - } - - // unstake or withdraw - message Reclaim { - uint64 bucketIndex = 1; - bytes payload = 2; - } - - // add the amount of bucket - message AddDeposit { - uint64 bucketIndex = 1; - string amount = 2; - bytes payload = 3; - } - - // restake the duration and autoStake flag of bucket - message Restake { - uint64 bucketIndex = 1; - uint32 stakedDuration = 2; - bool autoStake = 3; - bytes payload = 4; - } - - // move the bucket to vote for another candidate or transfer the ownership of bucket to another voters - message ChangeCandidate { - uint64 bucketIndex = 1; - string candidateName = 2; - bytes payload = 3; - } - - message TransferOwnership { - uint64 bucketIndex = 1; - string voterAddress = 2; - bytes payload = 3; - } - - message CandidateBasicInfo { - string name = 1; - string operatorAddress = 2; - string rewardAddress = 3; - } - - message CandidateRegister { - CandidateBasicInfo candidate = 1; - string stakedAmount = 2; - uint32 stakedDuration = 3; - bool autoStake = 4; - string ownerAddress = 5; // if ownerAddress is absent, owner of candidate is the sender - bytes payload = 6; - } - oneof message { - Create stakeCreate = 1; - Reclaim stakeUnstake = 2; - Reclaim stakeWithdraw = 3; - AddDeposit stakeAddDeposit = 4; - Restake stakeRestake = 5; - ChangeCandidate stakeChangeCandidate = 6; - TransferOwnership stakeTransferOwnership = 7; - CandidateRegister candidateRegister = 8; - CandidateBasicInfo candidateUpdate = 9; - } + // create stake + message Create { + // validator name + string candidateName = 1; + + // amount to be staked + string stakedAmount = 2; + + // duration + uint32 stakedDuration = 3; + + // auto-restake + bool autoStake = 4; + + // payload data + bytes payload = 5; + } + + // unstake or withdraw + message Reclaim { + // index to claim + uint64 bucketIndex = 1; + + // payload data + bytes payload = 2; + } + + // add the amount of bucket + message AddDeposit { + // index + uint64 bucketIndex = 1; + + // amount to add + string amount = 2; + + // payload data + bytes payload = 3; + } + + // restake the duration and autoStake flag of bucket + message Restake { + // index + uint64 bucketIndex = 1; + + // stake duration + uint32 stakedDuration = 2; + + // auto re-stake + bool autoStake = 3; + + // payload data + bytes payload = 4; + } + + // move the bucket to vote for another candidate or transfer the ownership of bucket to another voters + message ChangeCandidate { + // index + uint64 bucketIndex = 1; + + // validator name + string candidateName = 2; + + // payload data + bytes payload = 3; + } + + // transfer ownserhip of stake + message TransferOwnership { + // index + uint64 bucketIndex = 1; + + // address of voter + string voterAddress = 2; + + // payload data + bytes payload = 3; + } + + // Candidate (validator) info + message CandidateBasicInfo { + string name = 1; + string operatorAddress = 2; + string rewardAddress = 3; + } + + // Register a Candidate + message CandidateRegister { + CandidateBasicInfo candidate = 1; + string stakedAmount = 2; + uint32 stakedDuration = 3; + bool autoStake = 4; + string ownerAddress = 5; // if ownerAddress is absent, owner of candidate is the sender + bytes payload = 6; + } + + // the payload message + oneof message { + Create stakeCreate = 1; + Reclaim stakeUnstake = 2; + Reclaim stakeWithdraw = 3; + AddDeposit stakeAddDeposit = 4; + Restake stakeRestake = 5; + ChangeCandidate stakeChangeCandidate = 6; + TransferOwnership stakeTransferOwnership = 7; + CandidateRegister candidateRegister = 8; + CandidateBasicInfo candidateUpdate = 9; + } } +// Arbitrary contract call message ContractCall { + // amount string amount = 1; + + // contract address string contract = 2; + + // payload data bytes data = 3; } -// transaction signing input +// Input data necessary to create a signed transaction. message SigningInput { + // Transaction version uint32 version = 1; + + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 2; + + // Limit for the gas used uint64 gasLimit = 3; + + // Gas price string gasPrice = 4; + + // The secret private key used for signing (32 bytes). bytes privateKey = 5; + + // Payload transfer oneof action { Transfer transfer = 10; ContractCall call = 12; @@ -106,10 +169,10 @@ message SigningInput { Staking.TransferOwnership stakeTransferOwnership = 46; Staking.CandidateRegister candidateRegister = 47; Staking.CandidateBasicInfo candidateUpdate = 48; - } + } } -// transaction signing output +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded Action bytes bytes encoded = 1; @@ -118,29 +181,47 @@ message SigningOutput { bytes hash = 2; } +// An Action structure +// Used internally message ActionCore { - uint32 version = 1; - uint64 nonce = 2; - uint64 gasLimit = 3; - string gasPrice = 4; - oneof action { - Transfer transfer = 10; - ContractCall execution = 12; - // Native staking - Staking.Create stakeCreate = 40; - Staking.Reclaim stakeUnstake = 41; - Staking.Reclaim stakeWithdraw = 42; - Staking.AddDeposit stakeAddDeposit = 43; - Staking.Restake stakeRestake = 44; - Staking.ChangeCandidate stakeChangeCandidate = 45; - Staking.TransferOwnership stakeTransferOwnership = 46; - Staking.CandidateRegister candidateRegister = 47; - Staking.CandidateBasicInfo candidateUpdate = 48; - } + // version number + uint32 version = 1; + + // Nonce (should be larger than in the last transaction of the account) + uint64 nonce = 2; + + // Gas limit + uint64 gasLimit = 3; + + // Gas price + string gasPrice = 4; + + // action payload + oneof action { + Transfer transfer = 10; + ContractCall execution = 12; + // Native staking + Staking.Create stakeCreate = 40; + Staking.Reclaim stakeUnstake = 41; + Staking.Reclaim stakeWithdraw = 42; + Staking.AddDeposit stakeAddDeposit = 43; + Staking.Restake stakeRestake = 44; + Staking.ChangeCandidate stakeChangeCandidate = 45; + Staking.TransferOwnership stakeTransferOwnership = 46; + Staking.CandidateRegister candidateRegister = 47; + Staking.CandidateBasicInfo candidateUpdate = 48; + } } +// Signed Action +// Used internally message Action { - ActionCore core = 1; - bytes senderPubKey = 2; - bytes signature = 3; -} \ No newline at end of file + // Action details + ActionCore core = 1; + + // public key + bytes senderPubKey = 2; + + // the signature + bytes signature = 3; +} diff --git a/src/proto/NEAR.proto b/src/proto/NEAR.proto index 6a2641a2a45..a55a9079f94 100644 --- a/src/proto/NEAR.proto +++ b/src/proto/NEAR.proto @@ -3,64 +3,97 @@ syntax = "proto3"; package TW.NEAR.Proto; option java_package = "wallet.core.jni.proto"; +// Public key with type message PublicKey { + // Key type uint32 key_type = 1; + + // The public key data bytes data = 2; } +// Permissions for a function call message FunctionCallPermission { - bytes allowance = 1; // uint128 / little endian byte order + // uint128 / little endian byte order + bytes allowance = 1; + string receiver_id = 2; + repeated string method_names = 3; } +// Full access message FullAccessPermission { } +// Access key: nonce + permission message AccessKey { + // Nonce uint64 nonce = 1; + + // Permission oneof permission { FunctionCallPermission function_call = 2; FullAccessPermission full_access = 3; } } +// Create Account message CreateAccount { } +// Deploying a contract message DeployContract { bytes code = 1; } +// A method/function call message FunctionCall { + // Method/function name string method_name = 1; + + // input arguments bytes args = 2; + + // gas uint64 gas = 3; - bytes deposit = 4; // uint128 / little endian byte order + + // uint128 / little endian byte order + bytes deposit = 4; } +// Transfer message Transfer { - bytes deposit = 1; // uint128 / little endian byte order + // amount; uint128 / little endian byte order + bytes deposit = 1; } +// Stake message Stake { - bytes stake = 1; // uint128 / little endian byte order + // amount; uint128 / little endian byte order + bytes stake = 1; + + // owner public key PublicKey public_key = 2; } +// Add a key message AddKey { PublicKey public_key = 1; AccessKey access_key = 2; } +// Delete a key message DeleteKey { PublicKey public_key = 1; } +// Delete account message DeleteAccount { string beneficiary_id = 1; } +// Represents an action message Action { oneof payload { CreateAccount create_account = 1; @@ -76,18 +109,30 @@ message Action { // Input data necessary to create a signed order. message SigningInput { + // ID of the sender string signer_id = 1; + + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 2; + + // ID of the receiver string receiver_id = 3; + + // Recent block hash bytes block_hash = 4; + + // Payload action(s) repeated Action actions = 5; + // The secret private key used for signing (32 bytes). bytes private_key = 6; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed transaction blob bytes signed_transaction = 1; + + // Hash of the transaction bytes hash = 2; } diff --git a/src/proto/NEO.proto b/src/proto/NEO.proto index 0093145f29f..096327cbc51 100644 --- a/src/proto/NEO.proto +++ b/src/proto/NEO.proto @@ -5,35 +5,61 @@ option java_package = "wallet.core.jni.proto"; import "Common.proto"; +// Input for a transaction (output of a prev tx) message TransactionInput { + // Previous tx hash bytes prev_hash = 1; + + // Output index fixed32 prev_index = 2; // unspent value of UTXO int64 value = 3; + // Asset string asset_id = 4; } +// Output of a transaction message TransactionOutput { + // Asset string asset_id = 1; + + // Amount (as string) sint64 amount = 2; + + // destination address string to_address = 3; + + // change address string change_address = 4; } // Input data necessary to create a signed transaction. message SigningInput { + // Available transaction inputs repeated TransactionInput inputs = 1; + + // Transaction outputs repeated TransactionOutput outputs = 2; + + // The secret private key used for signing (32 bytes). bytes private_key = 3; + + // Fee int64 fee = 4; + + // Asset ID for gas string gas_asset_id = 5; + + // Address for the change string gas_change_address = 6; + + // Optional transaction plan (if missing it will be computed) TransactionPlan plan = 7; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; @@ -50,9 +76,16 @@ message TransactionOutputPlan { // Maximum available amount. int64 available_amount = 2; + // Amount that is left as change int64 change = 3; + + // Asset string asset_id = 4; + + // Destination address string to_address = 5; + + // Address for the change string change_address = 6; }; @@ -69,4 +102,4 @@ message TransactionPlan { // Optional error Common.Proto.SigningError error = 4; -}; \ No newline at end of file +}; diff --git a/src/proto/NULS.proto b/src/proto/NULS.proto index 2bcfd9d7a2e..51ed1806919 100644 --- a/src/proto/NULS.proto +++ b/src/proto/NULS.proto @@ -3,65 +3,122 @@ syntax = "proto3"; package TW.NULS.Proto; option java_package = "wallet.core.jni.proto"; +// Transaction from address message TransactionCoinFrom { + // Source address string from_address = 1; + + // Chain ID uint32 assets_chainid = 2; + + // ID of the asset uint32 assets_id = 3; - //tranaction out amount (256-bit number) + + // transaction out amount (256-bit number) bytes id_amount = 4; - //8 bytes + + // Nonce, 8 bytes bytes nonce = 5; - //lock status: 1 locked; 0 unlocked + + // lock status: 1 locked; 0 unlocked uint32 locked = 6; } +// Transaction to a destination message TransactionCoinTo { + // destination address string to_address = 1; + + // Chain ID uint32 assets_chainid = 2; + + // ID of the asset uint32 assets_id = 3; - // tranaction amount (256-bit number) + + // transaction amount (uint256, serialized little endian) bytes id_amount = 4; + + // lock time uint32 lock_time = 5; } +// A signature message Signature { + // Length of public key data uint32 pkey_len = 1; + + // The public key bytes public_key = 2; + + // The length of the signature uint32 sig_len = 3; + + // The signature data bytes signature = 4; } +// A transaction message Transaction { + // transaction type uint32 type = 1; + + // Timestamp of the transaction uint32 timestamp = 2; + + // Optional string remark string remark = 3; + + // The raw data bytes tx_data = 4; - //CoinFrom + + // CoinFrom TransactionCoinFrom input = 5; - //CoinTo + + // CoinTo TransactionCoinTo output = 6; + // Signature Signature tx_sigs = 7; + + // Tx hash uint32 hash = 8; } // Input data necessary to create a signed order. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + + // Source address string from = 2; + + // Destination address string to = 3; + + // Transfer amount (uint256, serialized little endian) bytes amount = 4; + + // Chain ID uint32 chain_id = 5; + + // Asset ID uint32 idassets_id = 6; - //The last 8 bytes of latest transaction hash + + // The last 8 bytes of latest transaction hash bytes nonce = 7; + + // Optional memo remark string remark = 8; + // Account balance bytes balance = 9; + // time, accurate to the second uint32 timestamp = 10; } +// Result containing the signed and encoded transaction. message SigningOutput { + // Encoded transaction bytes encoded = 1; -} \ No newline at end of file +} diff --git a/src/proto/Nano.proto b/src/proto/Nano.proto index 954c7930e28..9bd7a7a26bd 100644 --- a/src/proto/Nano.proto +++ b/src/proto/Nano.proto @@ -5,12 +5,13 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Private key + // The secret private key used for signing (32 bytes). bytes private_key = 1; // Optional parent block hash bytes parent_block = 2; + // Receive/Send reference oneof link_oneof { // Hash of a block to receive from bytes link_block = 3; @@ -28,12 +29,14 @@ message SigningInput { string work = 7; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signature bytes signature = 1; + // Block hash bytes block_hash = 2; - // Json representation of the block + + // JSON representation of the block string json = 3; } diff --git a/src/proto/Nebulas.proto b/src/proto/Nebulas.proto index fc8e7e9acf9..ea915410d59 100644 --- a/src/proto/Nebulas.proto +++ b/src/proto/Nebulas.proto @@ -8,42 +8,47 @@ message SigningInput { // sender's address. string from_address = 1; - // Chain identifier (256-bit number) + // Chain identifier (uint256, serialized little endian) bytes chain_id = 2; - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 3; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) bytes gas_price = 4; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 5; // Recipient's address. string to_address = 6; - // Amount to send in wei, 1 NAS = 10^18 Wei (256-bit number) + // Amount to send in wei, 1 NAS = 10^18 Wei (uint256, serialized little endian) bytes amount = 7; - // Timestamp to create transaction (256-bit number) + // Timestamp to create transaction (uint256, serialized little endian) bytes timestamp = 8; // Optional payload string payload = 9; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // Algorithm code uint32 algorithm = 1; + + // The signature bytes signature = 2; + + // Encoded transaction string raw = 3; } -// +// Generic data message Data { string type = 1; bytes payload = 2; @@ -51,17 +56,39 @@ message Data { // Raw transaction data message RawTransaction { + // tx hash bytes hash = 1; + + // source address bytes from = 2; + + // destination address bytes to = 3; + + // amount (uint256, serialized little endian) bytes value = 4; + + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 5; + + // transaction timestamp int64 timestamp = 6; + + // generic data Data data = 7; + + // chain ID (4 bytes) uint32 chain_id = 8; + + // gas price (uint256, serialized little endian) bytes gas_price = 9; + + // gas limit (uint256, serialized little endian) bytes gas_limit = 10; + // algorithm uint32 alg = 11; + + // signature bytes sign = 12; -} \ No newline at end of file +} diff --git a/src/proto/Nervos.proto b/src/proto/Nervos.proto index fd7c92afc6f..07cc53060d0 100644 --- a/src/proto/Nervos.proto +++ b/src/proto/Nervos.proto @@ -22,7 +22,7 @@ message TransactionPlan { // A list of 1 or more selected cells for this transaction repeated Cell selected_cells = 3; - // A list of 1 or more outputs by this transaciton + // A list of 1 or more outputs by this transaction repeated CellOutput outputs = 4; // A list of outputs data. @@ -74,6 +74,7 @@ message Script { bytes args = 3; } +// Transfer of native asset message NativeTransfer { // Recipient's address. string to_address = 1; @@ -88,6 +89,7 @@ message NativeTransfer { bool use_max_amount = 4; } +// Token transfer (SUDT) message SudtTransfer { // Recipient's address. string to_address = 1; @@ -105,6 +107,7 @@ message SudtTransfer { bool use_max_amount = 5; } +// Deposit message DaoDeposit { // Recipient's address. string to_address = 1; @@ -140,7 +143,7 @@ message SigningInput { // Transaction fee per byte. uint64 byte_fee = 1; - // Available private keys. + // The available secret private keys used for signing (32 bytes each). repeated bytes private_key = 2; // Available unspent cell outputs. @@ -149,6 +152,7 @@ message SigningInput { // Optional transaction plan TransactionPlan plan = 4; + // The payload transfer oneof operation_oneof { NativeTransfer native_transfer = 5; SudtTransfer sudt_transfer = 6; @@ -191,7 +195,7 @@ message Cell { bytes output_type = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Resulting transaction. Note that the amount may be different than the requested amount to account for fees and available funds. string transaction_json = 1; diff --git a/src/proto/Nimiq.proto b/src/proto/Nimiq.proto index 6c96981278d..76d00dd903e 100644 --- a/src/proto/Nimiq.proto +++ b/src/proto/Nimiq.proto @@ -5,18 +5,24 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + // Destination address string destination = 2; + // Amount of the transfer uint64 value = 3; + // Fee amount uint64 fee = 4; + // Validity start, in block height uint32 validity_start_height = 5; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // The encoded transaction bytes encoded = 1; } diff --git a/src/proto/Oasis.proto b/src/proto/Oasis.proto index 6b327044b58..316f6e295df 100644 --- a/src/proto/Oasis.proto +++ b/src/proto/Oasis.proto @@ -9,28 +9,39 @@ syntax = "proto3"; package TW.Oasis.Proto; option java_package = "wallet.core.jni.proto"; +// Transfer message TransferMessage { + // destination address string to = 1; + + // Gas price uint64 gas_price = 2; // Amount values strings prefixed by zero. e.g. "\u000010000000" string gas_amount = 3; + // Amount values strings prefixed by zero string amount = 4; + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 5; + + // Context, see https://docs.oasis.dev/oasis-core/common-functionality/crypto#domain-separation string context = 6; } +// Input data necessary to create a signed transaction. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + // Transfer payload oneof message { TransferMessage transfer = 2; } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Ontology.proto b/src/proto/Ontology.proto index 28992aacfcf..9496537dd18 100644 --- a/src/proto/Ontology.proto +++ b/src/proto/Ontology.proto @@ -5,32 +5,39 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - + // Contract ID, e.g. "ONT" string contract = 1; + // Method, e.g. "transfer" string method = 2; + // The secret private key used for signing (32 bytes). bytes owner_private_key = 3; // base58 encode address string (160-bit number) string to_address = 4; + // Transfer amount uint64 amount = 5; + // Private key of the payer bytes payer_private_key = 6; + // Gas price uint64 gas_price = 7; + // Limit for gas used uint64 gas_limit = 8; // base58 encode address string (160-bit number) string query_address = 9; + // Nonce (should be larger than in the last transaction of the account) uint32 nonce = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Polkadot.proto b/src/proto/Polkadot.proto index d46afa39725..178a26485de 100644 --- a/src/proto/Polkadot.proto +++ b/src/proto/Polkadot.proto @@ -3,17 +3,20 @@ syntax = "proto3"; package TW.Polkadot.Proto; option java_package = "wallet.core.jni.proto"; +// Known networks enum Network { POLKADOT = 0; KUSAMA = 2; } +// Destination options for reward enum RewardDestination { STAKED = 0; STASH = 1; CONTROLLER = 2; } +// An era, a period defined by a starting block and length message Era { // recent block number (called phase in polkadot code), should match block hash uint64 block_number = 1; @@ -22,52 +25,84 @@ message Era { uint64 period = 2; } +// Balance transfer transaction message Balance { + // transfer message Transfer { + // destination address string to_address = 1; - bytes value = 2; // big integer + + // amount (uint256, serialized little endian) + bytes value = 2; } oneof message_oneof { Transfer transfer = 1; } } +// Staking transaction message Staking { + // Bond to a controller message Bond { + // controller ID string controller = 1; + + // amount (uint256, serialized little endian) bytes value = 2; + + // destination for rewards RewardDestination reward_destination = 3; } + // Bond to a controller, with nominators message BondAndNominate { + // controller ID string controller = 1; + + // amount (uint256, serialized little endian) bytes value = 2; + + // destination for rewards RewardDestination reward_destination = 3; + + // list of nominators repeated string nominators = 4; } + // Bond extra amount message BondExtra { + // amount (uint256, serialized little endian) bytes value = 1; } + // Unbond message Unbond { + // amount (uint256, serialized little endian) bytes value = 1; } + // Withdraw unbonded amounts message WithdrawUnbonded { int32 slashing_spans = 1; } + // Nominate message Nominate { + // list of nominators repeated string nominators = 1; } + // Chill and unbound message ChillAndUnbond { + // amount (uint256, serialized little endian) bytes value = 1; } - message Chill {} + // Chill + message Chill { + } + // Payload messsage oneof message_oneof { Bond bond = 1; BondAndNominate bond_and_nominate = 2; @@ -85,28 +120,38 @@ message SigningInput { // Recent block hash, or genesis hash if era is not set bytes block_hash = 1; + // Genesis block hash (identifies the chain) bytes genesis_hash = 2; // Current account nonce uint64 nonce = 3; + // Specification version, e.g. 26. uint32 spec_version = 4; + + // Transaction version, e.g. 5. uint32 transaction_version = 5; - bytes tip = 6; // big integer + + // Optional tip to pay, big integer + bytes tip = 6; // Optional time validity limit, recommended, for replay-protection. Empty means Immortal. Era era = 7; + // The secret private key used for signing (32 bytes). bytes private_key = 8; + + // Network type Network network = 9; + // Payload message oneof message_oneof { Balance balance_call = 10; Staking staking_call = 11; } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Ripple.proto b/src/proto/Ripple.proto index 82f7ebd39a4..ffbb863741c 100644 --- a/src/proto/Ripple.proto +++ b/src/proto/Ripple.proto @@ -7,27 +7,37 @@ import "Common.proto"; // Input data necessary to create a signed transaction. message SigningInput { + // Transfer amount int64 amount = 1; + // Transfer fee int64 fee = 2; + // Account sequence number int32 sequence = 3; + // Ledger sequence number int32 last_ledger_sequence = 4; + // Source account string account = 5; + // Target account string destination = 6; + // A Destination Tag int64 destination_tag = 7; + // Transaction flags, optional int64 flags = 8; + // The secret private key used for signing (32 bytes). bytes private_key = 9; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // Encoded transaction bytes encoded = 1; // Optional error diff --git a/src/proto/Solana.proto b/src/proto/Solana.proto index 45ac259f631..0f7280b5da3 100644 --- a/src/proto/Solana.proto +++ b/src/proto/Solana.proto @@ -3,41 +3,62 @@ syntax = "proto3"; package TW.Solana.Proto; option java_package = "wallet.core.jni.proto"; +// Transfer transaction message Transfer { + // destination address string recipient = 1; + + // amount uint64 value = 2; - string memo = 3; // optional - repeated string references = 4; // optional referenced public keys + + // optional memo + string memo = 3; + + // optional referenced public keys + repeated string references = 4; } // Create and initialize a stake account, and delegate amount to it. // Recommendation behavior is to not specify a stake account, and a new unique account will be created each time. // Optionally a stake account pubkey can be specified, but it should not exist on chain. message DelegateStake { + // Validator's public key string validator_pubkey = 1; + + // delegation amount uint64 value = 2; + + // staking account string stake_account = 3; } // Deactivate staking on stake account message DeactivateStake { + // staking account string stake_account = 1; } // Deactivate staking on multiple stake account message DeactivateAllStake { + // staking accounts repeated string stake_accounts = 1; } // Withdraw amount from stake account message WithdrawStake { + // staking account string stake_account = 1; + + // withdrawal amount uint64 value = 2; } -// Techinical structure to group a staking account and an amount +// Technical structure to group a staking account and an amount message StakeAccountValue { + // staking account string stake_account = 1; + + // amount uint64 value = 2; } @@ -50,39 +71,74 @@ message WithdrawAllStake { message CreateTokenAccount { // main account -- can be same as signer, or other main account (if done on some other account's behalf) string main_address = 1; + + // Token minting address string token_mint_address = 2; + + // Token address string token_address = 3; } // Transfer tokens message TokenTransfer { + // Mint address of the token string token_mint_address = 1; + + // Source address string sender_token_address = 2; + + // Destination address string recipient_token_address = 3; + + // Amount uint64 amount = 4; - uint32 decimals = 5; // Note: 8-bit value - string memo = 6; // optional - repeated string references = 7; // optional referenced public keys + + // Note: 8-bit value + uint32 decimals = 5; + + // optional memo§ + string memo = 6; + + // optional referenced public keys + repeated string references = 7; } // CreateTokenAccount and TokenTransfer combined message CreateAndTransferToken { // main account -- can be same as signer, or other main account (if done on some other account's behalf) string recipient_main_address = 1; + + // Mint address of the token string token_mint_address = 2; + // Token address for the recipient, will be created first string recipient_token_address = 3; + + // Sender's token address string sender_token_address = 4; + + // amount uint64 amount = 5; - uint32 decimals = 6; // Note: 8-bit value - string memo = 7; // optional - repeated string references = 8; // optional referenced public keys + + // Note: 8-bit value + uint32 decimals = 6; + + // optional + string memo = 7; + + // optional referenced public keys + repeated string references = 8; } // Input data necessary to create a signed transaction. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + + // Relatively recent block hash string recent_blockhash = 2; + + // Payload message oneof transaction_type { Transfer transfer_transaction = 3; DelegateStake delegate_stake_transaction = 4; @@ -96,7 +152,8 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // The encoded transaction string encoded = 1; } diff --git a/src/proto/Stellar.proto b/src/proto/Stellar.proto index 0849f07097f..44a67e3672f 100644 --- a/src/proto/Stellar.proto +++ b/src/proto/Stellar.proto @@ -3,24 +3,28 @@ syntax = "proto3"; package TW.Stellar.Proto; option java_package = "wallet.core.jni.proto"; +// Represents an asset +// Note: alphanum12 currently not supported message Asset { // Optional in case of non-native asset; the asset issuer address string issuer = 1; // Optional in case of non-native asset; the asset alphanum4 code. string alphanum4 = 2; - - // Note: alphanum12 currently not supported } +// Create a new account message OperationCreateAccount { + // address string destination = 1; // Amount (*10^7) int64 amount = 2; } +// Perform payment message OperationPayment { + // Destination address string destination = 1; // Optional, can be left empty for native asset @@ -30,24 +34,32 @@ message OperationPayment { int64 amount = 3; } +// Change trust message OperationChangeTrust { + // The asset Asset asset = 1; // Validity (time bound to), unix time. Set to (now() + 2 * 365 * 86400) for 2 years; set to 0 for missing. int64 valid_before = 2; } +// A predicate (used in claim) +// Rest of predicates not currently supported +// See https://github.com/stellar/stellar-protocol/blob/master/core/cap-0023.md enum ClaimPredicate { Predicate_unconditional = 0; - // Rest of predicates not currently supported - // See https://github.com/stellar/stellar-protocol/blob/master/core/cap-0023.md } +// Claimant: account & predicate message Claimant { + // Claimant account string account = 1; + + // predicate ClaimPredicate predicate = 2; } +// Create a claimable balance (2-phase transfer) message OperationCreateClaimableBalance { // Optional, can be left empty for native asset Asset asset = 1; @@ -55,42 +67,53 @@ message OperationCreateClaimableBalance { // Amount (*10^7) int64 amount = 2; + // One or more claimants repeated Claimant claimants = 3; } +// Claim a claimable balance message OperationClaimClaimableBalance { // 32-byte balance ID hash bytes balance_id = 1; } +// Empty memo (placeholder) message MemoVoid { } +// Memo with text message MemoText { string text = 1; } +// Memo with an ID message MemoId { int64 id = 1; } +// Memo with a hash message MemoHash { bytes hash = 1; } // Input data necessary to create a signed transaction. message SigningInput { + // Transaction fee int32 fee = 1; + // Account sequence int64 sequence = 2; + // Source account string account = 3; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 4; + // Wellknown passphrase, specific to the chain string passphrase = 5; + // Payload message oneof operation_oneof { OperationCreateAccount op_create_account = 6; OperationPayment op_payment = 7; @@ -99,6 +122,7 @@ message SigningInput { OperationClaimClaimableBalance op_claim_claimable_balance = 15; } + // Memo oneof memo_type_oneof { MemoVoid memo_void = 9; MemoText memo_text = 10; @@ -108,7 +132,7 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signature. string signature = 1; diff --git a/src/proto/THORChainSwap.proto b/src/proto/THORChainSwap.proto index a5c943b4a65..7f3a20396c0 100644 --- a/src/proto/THORChainSwap.proto +++ b/src/proto/THORChainSwap.proto @@ -17,7 +17,8 @@ enum Chain { // Predefined error codes enum ErrorCode { - OK = 0; // OK + // OK + OK = 0; Error_general = 1; Error_Input_proto_deserialization = 2; Error_Unsupported_from_chain = 13; @@ -30,14 +31,22 @@ enum ErrorCode { // An error code + a free text message Error { + // code of the error ErrorCode code = 1; + + // optional error message string message = 2; } // Represents an asset. Examples: BNB.BNB, RUNE.RUNE, BNB.RUNE-67C message Asset { + // Chain ID Chain chain = 1; + + // Symbol string symbol = 2; + + // The ID of the token (blockchain-specific format) string token_id = 3; } @@ -68,11 +77,15 @@ message SwapInput { string to_amount_limit = 8; } +// Result of the swap, a SigningInput struct for the specific chain message SwapOutput { + // Source chain Chain from_chain = 1; + + // Destination chain Chain to_chain = 2; - // Error code, filled in case of error, OK&"" on success + // Error code, filled in case of error, OK/empty on success Error error = 3; // Prepared unsigned transaction input, on the source chain, to THOR. Some fields must be completed, and it has to be signed. diff --git a/src/proto/Tezos.proto b/src/proto/Tezos.proto index f5d9240e1e7..7a56c4d2ab5 100644 --- a/src/proto/Tezos.proto +++ b/src/proto/Tezos.proto @@ -6,32 +6,49 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed Tezos transaction. // Next field: 3 message SigningInput { + // One or more operations OperationList operation_list = 1; + + // The secret private key used for signing (32 bytes). bytes private_key = 2; } -// Transaction signing output. +// Result containing the signed and encoded transaction. // Next field: 2 message SigningOutput { + // The encoded transaction bytes encoded = 1; } // A list of operations and a branch. // Next field: 3 message OperationList { + // branch string branch = 1; + + // list of operations repeated Operation operations = 2; } // An operation that can be applied to the Tezos blockchain. // Next field: 12 message Operation { + // counter int64 counter = 1; + + // source account string source = 2; + + // fee int64 fee = 3; + + // gas limit int64 gas_limit = 4; + + // storage limit int64 storage_limit = 5; + // Operation types enum OperationKind { // Note: Proto3 semantics require a zero value. ENDORSEMENT = 0; @@ -40,6 +57,7 @@ message Operation { TRANSACTION = 108; DELEGATION = 110; } + // Operation type OperationKind kind = 7; // Operation specific data depending on the type of the operation. @@ -73,6 +91,7 @@ message FA2Parameters { repeated TxObject txs_object = 2; } +// Generic operation parameters message OperationParameters { oneof parameters { FA12Parameters fa12_parameters = 1; diff --git a/src/proto/Theta.proto b/src/proto/Theta.proto index f779c0b5b0d..4647d89ba83 100644 --- a/src/proto/Theta.proto +++ b/src/proto/Theta.proto @@ -11,23 +11,23 @@ message SigningInput { /// Recipient address string to_address = 2; - /// Theta token amount to send in wei (256-bit number) + /// Theta token amount to send in wei (uint256, serialized little endian) bytes theta_amount = 3; - /// TFuel token amount to send in wei (256-bit number) + /// TFuel token amount to send in wei (uint256, serialized little endian) bytes tfuel_amount = 4; /// Sequence number of the transaction for the sender address uint64 sequence = 5; - /// Fee amount in TFuel wei for the transaction (256-bit number) + /// Fee amount in TFuel wei for the transaction (uint256, serialized little endian) bytes fee = 6; - /// Private key + /// The secret private key used for signing (32 bytes). bytes private_key = 7; } -/// Transaction signing output +// Result containing the signed and encoded transaction. message SigningOutput { /// Signed and encoded transaction bytes bytes encoded = 1; diff --git a/src/proto/Tron.proto b/src/proto/Tron.proto index 3a6b635b169..7ed7ce37723 100644 --- a/src/proto/Tron.proto +++ b/src/proto/Tron.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package TW.Tron.Proto; option java_package = "wallet.core.jni.proto"; +// A transfer transaction message TransferContract { // Sender address. string owner_address = 1; @@ -14,6 +15,7 @@ message TransferContract { int64 amount = 3; } +// Asset transfer message TransferAssetContract { // Asset name. string asset_name = 1; @@ -28,6 +30,7 @@ message TransferAssetContract { int64 amount = 4; } +// TRC20 token transfer message TransferTRC20Contract { // Contract name. string contract_address = 1; @@ -38,79 +41,125 @@ message TransferTRC20Contract { // Recipient address. string to_address = 3; - // Amount to send, uint256, big-endian. + // Amount to send, (uint256, serialized big endian) bytes amount = 4; } +// Freeze balance message FreezeBalanceContract { // Sender address. string owner_address = 1; + // Frozen balance. Minimum 1 int64 frozen_balance = 2; + // Frozen duration int64 frozen_duration = 3; + // Resource type: BANDWIDTH | ENERGY string resource = 10; + // Receiver address string receiver_address = 15; } +// Unfreeze balance message UnfreezeBalanceContract { // Sender address string owner_address = 1; + // Resource type: BANDWIDTH | ENERGY string resource = 10; + // Receiver address string receiver_address = 15; } +// Unfreeze asset message UnfreezeAssetContract { // Sender address string owner_address = 1; } +// Vote asset message VoteAssetContract { // Sender address string owner_address = 1; + // Vote addresses repeated string vote_address = 2; + bool support = 3; + int32 count = 5; } +// Vote witness message VoteWitnessContract { + // A vote message Vote { + // address string vote_address = 1; + + // vote count int64 vote_count = 2; } + + // Owner string owner_address = 1; + + // The votes repeated Vote votes = 2; + bool support = 3; } +// Withdraw balance message WithdrawBalanceContract { // Sender address string owner_address = 1; } +// Smart contract call message TriggerSmartContract { + // Owner string owner_address = 1; + + // Contract address string contract_address = 2; + + // amount int64 call_value = 3; + + // call data bytes data = 4; + + // token value int64 call_token_value = 5; + + // ID of the token int64 token_id = 6; } +// Info from block header message BlockHeader { + // creation timestamp int64 timestamp = 1; + + // root bytes tx_trie_root = 2; + + // hash of the parent bytes parent_hash = 3; + int64 number = 7; + bytes witness_address = 9; + int32 version = 10; } +// Transaction message Transaction { // Transaction timestamp in milliseconds. int64 timestamp = 1; @@ -139,15 +188,16 @@ message Transaction { } } +// Input data necessary to create a signed transaction. message SigningInput { // Transaction. Transaction transaction = 1; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 2; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Transaction identifier. bytes id = 1; @@ -158,5 +208,6 @@ message SigningOutput { bytes ref_block_bytes = 3; bytes ref_block_hash = 4; + // Result in JSON string json = 5; } diff --git a/src/proto/VeChain.proto b/src/proto/VeChain.proto index c89d9f0bfc5..2b2b7e151c2 100644 --- a/src/proto/VeChain.proto +++ b/src/proto/VeChain.proto @@ -3,11 +3,12 @@ syntax = "proto3"; package TW.VeChain.Proto; option java_package = "wallet.core.jni.proto"; +// A clause, between a sender and destination message Clause { /// Recipient address. string to = 1; - /// Transaction amount. + /// Transaction amount (uint256, serialized little endian) bytes value = 2; /// Payload data. @@ -43,11 +44,11 @@ message SigningInput { /// Number set by user. uint64 nonce = 8; - // Private key. + /// The secret private key used for signing (32 bytes). bytes private_key = 9; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Waves.proto b/src/proto/Waves.proto index 011fcf1138b..a2acc2ef599 100644 --- a/src/proto/Waves.proto +++ b/src/proto/Waves.proto @@ -3,29 +3,45 @@ syntax = "proto3"; package TW.Waves.Proto; option java_package = "wallet.core.jni.proto"; -//Transfer transaction +// Transfer transaction message TransferMessage { + // amount int64 amount = 1; + + // asset ID string asset = 2; + // minimum 0.001 Waves (100000 Wavelets) for now int64 fee = 3; + + // asset of the fee string fee_asset = 4; + + // destination address string to = 5; + // any 140 bytes payload, will be displayed to the client as utf-8 string bytes attachment = 6; } -//Lease transaction +// Lease transaction message LeaseMessage { + // amount int64 amount = 1; + + // destination string to = 2; + // minimum 0.001 Waves (100000 Wavelets) for now int64 fee = 3; } -//Lease transaction +// Cancel Lease transaction message CancelLeaseMessage { + // Lease ID to cancel string lease_id = 1; + + // Fee used int64 fee = 2; } @@ -34,7 +50,11 @@ message CancelLeaseMessage { message SigningInput { // in millis int64 timestamp = 1; + + // The secret private key used for signing (32 bytes). bytes private_key = 2; + + // Payload message oneof message_oneof { TransferMessage transfer_message = 3; LeaseMessage lease_message = 4; @@ -42,9 +62,12 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // signature data bytes signature = 1; + + // transaction in JSON format string json = 2; } diff --git a/src/proto/Zilliqa.proto b/src/proto/Zilliqa.proto index ea5d3eff4c9..03e5e2167fa 100644 --- a/src/proto/Zilliqa.proto +++ b/src/proto/Zilliqa.proto @@ -3,14 +3,17 @@ syntax = "proto3"; package TW.Zilliqa.Proto; option java_package = "wallet.core.jni.proto"; +// Generic transaction message Transaction { + // Transfer transaction message Transfer { - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) bytes amount = 1; } + // Generic contract call message Raw { - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) bytes amount = 1; // Smart contract code @@ -43,13 +46,14 @@ message SigningInput { // GasLimit uint64 gas_limit = 5; - // Private Key + // The secret private key used for signing (32 bytes). bytes private_key = 6; + // The payload transaction Transaction transaction = 7; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed signature bytes. bytes signature = 1; diff --git a/tests/FIO/TWFIOTests.cpp b/tests/FIO/TWFIOTests.cpp index acbdf2e889a..4460eff762a 100644 --- a/tests/FIO/TWFIOTests.cpp +++ b/tests/FIO/TWFIOTests.cpp @@ -146,7 +146,7 @@ TEST(TWFIO, NewFundsRequest) { Proto::SigningOutput output; ANY_SIGN(input, TWCoinTypeFIO); - // Packed transacton varies, as there is no way to control encryption IV parameter from this level. + // Packed transaction varies, as there is no way to control encryption IV parameter from this level. // Therefore full equality cannot be checked, tail is cut off. The first N chars are checked, works in this case. EXPECT_EQ( R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff000000000100403ed4aa0ba85b00acba384dbdb89a01102b2f46fca756b200000000a8ed32328802106d6172696f4066696f746573746)", diff --git a/tools/doxygen_convert_comments b/tools/doxygen_convert_comments index 3c76e250487..91870ffb466 100755 --- a/tools/doxygen_convert_comments +++ b/tools/doxygen_convert_comments @@ -16,7 +16,7 @@ function process_swift_comments() { function swift_convert() { - echo "Processing swift convertion" + echo "Processing swift conversion" for d in $SWIFT_FOLDER_PATH ; do process_swift_comments $d From 0fdab0adfebfb85b737cd909b87fe7137455d16e Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Mon, 3 Oct 2022 15:32:59 +0100 Subject: [PATCH 006/426] Add zkSync v2 evm (#2605) * Add zkSync v2 evm --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 30 ++++++++++++++++ swift/Tests/CoinAddressDerivationTests.swift | 1 + tests/CoinAddressDerivationTests.cpp | 1 + tests/ZkSyncV2/TWCoinTypeTests.cpp | 36 +++++++++++++++++++ 7 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/ZkSyncV2/TWCoinTypeTests.cpp 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 34765728c77..98f69f55d1b 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 @@ -40,7 +40,7 @@ class CoinAddressDerivationTests { CALLISTO -> assertEquals("0x3E6FFC80745E6669135a76F4A7ce6BCF02436e04", address) DASH -> assertEquals("XqHiz8EXYbTAtBEYs4pWTHh7ipEDQcNQeT", address) DIGIBYTE -> assertEquals("dgb1qtjgmerfqwdffyf8ghcrkgy52cghsqptynmyswu", address) - ETHEREUM, SMARTCHAIN, POLYGON, OPTIMISM, ARBITRUM, ECOCHAIN, AVALANCHECCHAIN, XDAI, + ETHEREUM, SMARTCHAIN, POLYGON, OPTIMISM, ZKSYNC, ARBITRUM, ECOCHAIN, AVALANCHECCHAIN, XDAI, FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) diff --git a/docs/registry.md b/docs/registry.md index 2bc175c03ad..5d89f556a91 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -80,6 +80,7 @@ This list is generated from [./registry.json](../registry.json) | 10000118 | Osmosis | OSMO | | | | 10000145 | Smart Bitcoin Cash | BCH | | | | 10000250 | Fantom | FTM | | | +| 10000280 | zkSync v2 | ETH | | | | 10000288 | Boba | BOBAETH | | | | 10000321 | KuCoin Community Chain | KCS | | | | 10000330 | Terra | LUNA | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 831f9c3124a..52e5f2d50f5 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -90,6 +90,7 @@ enum TWCoinType { TWCoinTypeTHORChain = 931, TWCoinTypeBluzelle = 483, TWCoinTypeOptimism = 10000070, + TWCoinTypeZksync = 10000280, TWCoinTypeArbitrum = 10042221, TWCoinTypeECOChain = 10000553, TWCoinTypeAvalancheCChain = 10009000, diff --git a/registry.json b/registry.json index 2c8dcb73ebe..a2e07e2c29b 100644 --- a/registry.json +++ b/registry.json @@ -2061,6 +2061,36 @@ "documentation": "https://eth.wiki/json-rpc/API" } }, + { + "id": "zksync", + "name": "Zksync", + "displayName": "zkSync v2", + "coinId": 10000280, + "slip44": 60, + "symbol": "ETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "280", + "addressHasher": "keccak256", + "explorer": { + "url": "https://zksync2-testnet.zkscan.io", + "txPath": "/tx/", + "accountPath": "/address/" + }, + "info": { + "url": "https://portal.zksync.io/", + "source": "https://github.com/matter-labs/zksync", + "rpc": "https://zksync2-testnet.zksync.dev", + "documentation": "https://v2-docs.zksync.io" + } + }, { "id": "arbitrum", "name": "Arbitrum", diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 789a9a6627d..5dd2b6d4ef4 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -74,6 +74,7 @@ class CoinAddressDerivationTests: XCTestCase { .smartChain, .polygon, .optimism, + .zksync, .arbitrum, .ecochain, .avalancheCChain, diff --git a/tests/CoinAddressDerivationTests.cpp b/tests/CoinAddressDerivationTests.cpp index 6d04e729eba..5bd9acd5d63 100644 --- a/tests/CoinAddressDerivationTests.cpp +++ b/tests/CoinAddressDerivationTests.cpp @@ -57,6 +57,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeMoonbeam: case TWCoinTypeMoonriver: case TWCoinTypeOptimism: + case TWCoinTypeZksync: case TWCoinTypeOKXChain: case TWCoinTypePOANetwork: case TWCoinTypePolygon: diff --git a/tests/ZkSyncV2/TWCoinTypeTests.cpp b/tests/ZkSyncV2/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..9b00c8112a6 --- /dev/null +++ b/tests/ZkSyncV2/TWCoinTypeTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2022 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 "../interface/TWTestUtilities.h" +#include +#include + +namespace TW::TWZksync::tests { + +TEST(TWZksyncCoinType, TWCoinType) { + const auto coin = TWCoinTypeZksync; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb526861291c0335435e3c976e672a464b70762e54d7167409fb4f66e374ed708")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x970978989a51790ee591b2a54f92c7cd9cdc2f88")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "zksync"); + assertStringsEqual(name, "zkSync v2"); + assertStringsEqual(symbol, "ETH"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "280"); + assertStringsEqual(txUrl, "https://zksync2-testnet.zkscan.io/tx/0xb526861291c0335435e3c976e672a464b70762e54d7167409fb4f66e374ed708"); + assertStringsEqual(accUrl, "https://zksync2-testnet.zkscan.io/address/0x970978989a51790ee591b2a54f92c7cd9cdc2f88"); +} + +} // namespace TW::TWZksync::tests From 61157af882b24b74310f72745fd2598568a965ba Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Tue, 4 Oct 2022 14:10:07 +0100 Subject: [PATCH 007/426] fix(codegen): correct swift template for _Nullable string parameter handling (#2609) --- codegen/lib/templates/swift/parameter_access.erb | 8 +++++--- include/TrustWalletCore/TWBase32.h | 12 ++++++------ src/interface/TWBase32.cpp | 14 ++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/codegen/lib/templates/swift/parameter_access.erb b/codegen/lib/templates/swift/parameter_access.erb index 8521c18bb4a..ddf4335753f 100644 --- a/codegen/lib/templates/swift/parameter_access.erb +++ b/codegen/lib/templates/swift/parameter_access.erb @@ -10,12 +10,14 @@ let <%= param.name %>String: UnsafeRawPointer? if let s = <%= param.name %> { <%= param.name %>String = TWStringCreateWithNSString(s) - defer { - TWStringDelete(s) - } } else { <%= param.name %>String = nil } + defer { + if let s = <%= param.name %>String { + TWStringDelete(s) + } + } <% else -%> let <%= param.name %>String = TWStringCreateWithNSString(<%= param.name %>) defer { diff --git a/include/TrustWalletCore/TWBase32.h b/include/TrustWalletCore/TWBase32.h index 871bbf8b561..e65c67e5890 100644 --- a/include/TrustWalletCore/TWBase32.h +++ b/include/TrustWalletCore/TWBase32.h @@ -19,34 +19,34 @@ struct TWBase32; /// Decode a Base32 input with the given alphabet /// /// \param string Encoded base32 input to be decoded -/// \param alphabet Decode with the given alphabet, if empty ALPHABET_RFC4648 is used by default +/// \param alphabet Decode with the given alphabet, if nullptr ALPHABET_RFC4648 is used by default /// \return The decoded data, can be null. /// \note ALPHABET_RFC4648 doesn't support padding in the default alphabet TW_EXPORT_STATIC_METHOD -TWData* _Nullable TWBase32DecodeWithAlphabet(TWString* _Nonnull string, TWString* _Nonnull alphabet); +TWData* _Nullable TWBase32DecodeWithAlphabet(TWString* _Nonnull string, TWString* _Nullable alphabet); /// Decode a Base32 input with the default alphabet (ALPHABET_RFC4648) /// /// \param string Encoded input to be decoded /// \return The decoded data -/// \note Call TWBase32DecodeWithAlphabet with empty string. +/// \note Call TWBase32DecodeWithAlphabet with nullptr. TW_EXPORT_STATIC_METHOD TWData* _Nullable TWBase32Decode(TWString* _Nonnull string); /// Encode an input to Base32 with the given alphabet /// /// \param data Data to be encoded (raw bytes) -/// \param alphabet Encode with the given alphabet, if empty ALPHABET_RFC4648 is used by default +/// \param alphabet Encode with the given alphabet, if nullptr ALPHABET_RFC4648 is used by default /// \return The encoded data /// \note ALPHABET_RFC4648 doesn't support padding in the default alphabet TW_EXPORT_STATIC_METHOD -TWString *_Nonnull TWBase32EncodeWithAlphabet(TWData *_Nonnull data, TWString* _Nonnull alphabet); +TWString *_Nonnull TWBase32EncodeWithAlphabet(TWData *_Nonnull data, TWString* _Nullable alphabet); /// Encode an input to Base32 with the default alphabet (ALPHABET_RFC4648) /// /// \param data Data to be encoded (raw bytes) /// \return The encoded data -/// \note Call TWBase32EncodeWithAlphabet with empty string. +/// \note Call TWBase32EncodeWithAlphabet with nullptr. TW_EXPORT_STATIC_METHOD TWString *_Nonnull TWBase32Encode(TWData *_Nonnull data); diff --git a/src/interface/TWBase32.cpp b/src/interface/TWBase32.cpp index 11fd93093c8..6d2e89c342c 100644 --- a/src/interface/TWBase32.cpp +++ b/src/interface/TWBase32.cpp @@ -12,11 +12,11 @@ using namespace TW; -TWData* TWBase32DecodeWithAlphabet(TWString* _Nonnull string, TWString* _Nonnull alphabet) { +TWData* TWBase32DecodeWithAlphabet(TWString* _Nonnull string, TWString* _Nullable alphabet) { Data decodedOut; auto cppString = *reinterpret_cast(string); const char* alphabetRaw = nullptr; - if (TWStringSize(alphabet) > 0) { + if (alphabet != nullptr) { alphabetRaw = TWStringUTF8Bytes(alphabet); } auto result = Base32::decode(cppString, decodedOut, alphabetRaw); @@ -24,14 +24,13 @@ TWData* TWBase32DecodeWithAlphabet(TWString* _Nonnull string, TWString* _Nonnull } TWData* _Nullable TWBase32Decode(TWString* _Nonnull string) { - std::string empty; - return TWBase32DecodeWithAlphabet(string, &empty); + return TWBase32DecodeWithAlphabet(string, nullptr); } -TWString* _Nonnull TWBase32EncodeWithAlphabet(TWData* _Nonnull data, TWString* _Nonnull alphabet) { +TWString* _Nonnull TWBase32EncodeWithAlphabet(TWData* _Nonnull data, TWString* _Nullable alphabet) { auto cppData = *reinterpret_cast(data); const char* alphabetRaw = nullptr; - if (TWStringSize(alphabet) > 0) { + if (alphabet != nullptr) { alphabetRaw = TWStringUTF8Bytes(alphabet); } auto result = Base32::encode(cppData, alphabetRaw); @@ -39,6 +38,5 @@ TWString* _Nonnull TWBase32EncodeWithAlphabet(TWData* _Nonnull data, TWString* _ } TWString* _Nonnull TWBase32Encode(TWData* _Nonnull data) { - std::string empty; - return TWBase32EncodeWithAlphabet(data, &empty); + return TWBase32EncodeWithAlphabet(data, nullptr); } From 269dd3aca8f9db7e2cb17299c8cf4c36969fff97 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Fri, 7 Oct 2022 00:13:18 +0200 Subject: [PATCH 008/426] Support Bitcoin testnet address (#2621) * Support Bitcoin testnet address * Put prefix in constant --- registry.json | 6 ++++ src/Bitcoin/Entry.cpp | 3 ++ src/Bitcoin/SegwitAddress.h | 6 ++++ tests/Bitcoin/SegwitAddressTests.cpp | 42 ++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/registry.json b/registry.json index a2e07e2c29b..d77105beebe 100644 --- a/registry.json +++ b/registry.json @@ -18,6 +18,12 @@ "path": "m/44'/0'/0'/0/0", "xpub": "xpub", "xprv": "xprv" + }, + { + "name": "testnet", + "path": "m/84'/1'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" } ], "curve": "secp256k1", diff --git a/src/Bitcoin/Entry.cpp b/src/Bitcoin/Entry.cpp index 26816b632b7..36ee93a94f6 100644 --- a/src/Bitcoin/Entry.cpp +++ b/src/Bitcoin/Entry.cpp @@ -72,6 +72,9 @@ std::string Entry::deriveAddress(TWCoinType coin, TWDerivation derivation, const case TWDerivationLitecoinLegacy: return Address(publicKey, p2pkh).string(); + case TWDerivationBitcoinTestnet: + return SegwitAddress::createTestnetFromPublicKey(publicKey).string(); + case TWDerivationBitcoinSegwit: case TWDerivationDefault: default: diff --git a/src/Bitcoin/SegwitAddress.h b/src/Bitcoin/SegwitAddress.h index 8a073b6f1fa..54803acb7ba 100644 --- a/src/Bitcoin/SegwitAddress.h +++ b/src/Bitcoin/SegwitAddress.h @@ -31,6 +31,9 @@ class SegwitAddress { /// Witness program. Data witnessProgram; + // Prefix for Bitcoin Testnet Segwit addresses + static constexpr auto TestnetPrefix = "tb"; + /// Determines whether a string makes a valid Bech32 address. static bool isValid(const std::string& string); @@ -47,6 +50,9 @@ class SegwitAddress { /// Taproot (v>=1) is not supported by this method. SegwitAddress(const PublicKey& publicKey, std::string hrp); + /// Create a testnet address + static SegwitAddress createTestnetFromPublicKey(const PublicKey& publicKey) { return SegwitAddress(publicKey, TestnetPrefix); } + /// Decodes a SegWit address. /// /// \returns a tuple with the address, hrp, and a success flag. diff --git a/tests/Bitcoin/SegwitAddressTests.cpp b/tests/Bitcoin/SegwitAddressTests.cpp index 4545ff9dc86..3180eef8337 100644 --- a/tests/Bitcoin/SegwitAddressTests.cpp +++ b/tests/Bitcoin/SegwitAddressTests.cpp @@ -6,6 +6,7 @@ #include "Bech32.h" #include "Bitcoin/SegwitAddress.h" +#include "HDWallet.h" #include "HexCoding.h" #include @@ -208,4 +209,45 @@ TEST(SegwitAddress, Equals) { ASSERT_FALSE(addr1 == addr2); } +TEST(SegwitAddress, TestnetAddress) { + const auto mnemonic1 = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; + const auto passphrase = ""; + const auto coin = TWCoinTypeBitcoin; + HDWallet wallet = HDWallet(mnemonic1, passphrase); + + // default + { + const auto privKey = wallet.getKey(coin, TWDerivationDefault); + const auto pubKey = privKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(pubKey.bytes), "02df9ef2a7a5552765178b181e1e1afdefc7849985c7dfe9647706dd4fa40df6ac"); + EXPECT_EQ(SegwitAddress(pubKey, "bc").string(), "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + } + + // testnet: different derivation path and hrp + { + const auto privKey = wallet.getKey(coin, TW::DerivationPath("m/84'/1'/0'/0/0")); + const auto pubKey = privKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(pubKey.bytes), "03eb1a535b59f03894b99319f850c784cf4164f4de07620695c5cf0dc5c1ab2a54"); + EXPECT_EQ(SegwitAddress::createTestnetFromPublicKey(pubKey).string(), "tb1qq8p994ak933c39d2jaj8n4sg598tnkhnyk5sg5"); + // alternative with custom hrp + EXPECT_EQ(SegwitAddress(pubKey, "tb").string(), "tb1qq8p994ak933c39d2jaj8n4sg598tnkhnyk5sg5"); + } + + EXPECT_TRUE(SegwitAddress::isValid("tb1qq8p994ak933c39d2jaj8n4sg598tnkhnyk5sg5")); +} + +TEST(SegwitAddress, SegwitDerivationHDWallet) { + const auto mnemonic1 = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; + const auto passphrase = ""; + const auto coin = TWCoinTypeBitcoin; + HDWallet wallet = HDWallet(mnemonic1, passphrase); + + // addresses with different derivations + EXPECT_EQ(wallet.deriveAddress(coin), "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + EXPECT_EQ(wallet.deriveAddress(coin, TWDerivationDefault), "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + EXPECT_EQ(wallet.deriveAddress(coin, TWDerivationBitcoinSegwit), "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + EXPECT_EQ(wallet.deriveAddress(coin, TWDerivationBitcoinLegacy), "1GVb4mfQrvymPLz7zeZ3LnQ8sFv3NedZXe"); + EXPECT_EQ(wallet.deriveAddress(coin, TWDerivationBitcoinTestnet), "tb1qq8p994ak933c39d2jaj8n4sg598tnkhnyk5sg5"); +} + } // namespace TW::Bitcoin::tests From 22bfd4303f149dda1ddef83b3bb3204812b9b167 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:09:24 +0200 Subject: [PATCH 009/426] [Devconsole] Add missing walletXxx utilities to help (#2616) * Add missing wallet* utilities to help * Fix return types (review comment) --- samples/typescript/devconsole.ts/src/index.ts | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/samples/typescript/devconsole.ts/src/index.ts b/samples/typescript/devconsole.ts/src/index.ts index fd994c1f539..f08c79bd6c8 100644 --- a/samples/typescript/devconsole.ts/src/index.ts +++ b/samples/typescript/devconsole.ts/src/index.ts @@ -132,9 +132,9 @@ async function main() { autoFillName(name: string): string { return (name === '') ? `Wallet${(this.storeWallets.length + 1).toString()}` : name; } - async create(strength: number, name: string): Promise { + async create(strength: number, name: string): Promise { this.wallet = null; - if (!this.keystore) { return; } + if (!this.keystore) { return null; } try { if (name === '') { name = this.autoFillName(name); } const hdWallet = WC.HDWallet.create(strength, this.HDWalletPassord); @@ -150,11 +150,12 @@ async function main() { return this.wallet; } catch (err) { console.log(`Exception: ${err}`); + return null; } } - async import(mnemonic: string, name: string): Promise { + async import(mnemonic: string, name: string): Promise { this.wallet = null; - if (!this.keystore) { return; } + if (!this.keystore) { return null; } try { if (!WC.Mnemonic.isValid(mnemonic)) { console.error(`Mnemonic is not valid ${mnemonic}`); @@ -171,16 +172,18 @@ async function main() { return this.wallet; } catch (err) { console.log(`Exception: ${err}`); + return null; } } - async addCoin(coin: string): Promise { + async addCoin(coin: string): Promise { if (this.wallet == null) { console.error('No wallet open, see walletCreate() / walletLoad() / walletsList()'); - return; + return null; } - if (!this.keystore) { return; } + if (!this.keystore) { return null; } const wallet = await this.keystore.addAccounts(this.wallet?.wallet.identifier(), this.StoreFixedPassword, [coin]); this.wallet = new StoredKeyWallet(this.keystore.mapStoredKey(wallet)); + return this.wallet; } // Delete loaded wallet async delete(param: string): Promise { @@ -248,9 +251,9 @@ async function main() { async function walletsList() { await wallets.list(); } async function walletLoad(index: number) { await wallets.load(index); } async function walletsDeleteAll(param: string) { await wallets.deleteAll(param); } - async function walletCreate(strength: number = 256, name: string = ''): Promise { return await wallets.create(strength, name); } - async function walletImport(mnemonic: string, name: string = ''): Promise { return wallets.import(mnemonic, name); } - async function walletAddCoin(coin: string): Promise { return wallets.addCoin(coin); } + async function walletCreate(strength: number = 256, name: string = ''): Promise { return await wallets.create(strength, name); } + async function walletImport(mnemonic: string, name: string = ''): Promise { return await wallets.import(mnemonic, name); } + async function walletAddCoin(coin: string): Promise { return await wallets.addCoin(coin); } function walletDump(): void { wallets.dump(); } async function walletDelete(param: string) { await wallets.delete(param); } @@ -276,6 +279,11 @@ async function main() { console.log(" walletCreate([strength], [name]) walletCreate(128)"); console.log(" walletImport(mnemonic, [name]) walletImport('ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal', 'Test1')"); console.log(" walletDump() walletDump()"); + console.log(" walletAddCoin(symbol) walletAddCoin(CoinType.solana)"); + console.log(" walletsList() walletsList()"); + console.log(" walletLoad() walletLoad(0)"); + console.log(" walletDelete() walletDelete('delete')"); + console.log(" walletsDeleteAll() walletsDeleteAll('deleteall')"); console.log(); console.log("See examples in samplescripts folder, e.g.: cat samplescripts/protoeth.sampleinput.txt | npm run start"); console.log(); From 871c4d51d32dddfb6003f8fc204925cd039c068d Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 7 Oct 2022 12:46:25 +0200 Subject: [PATCH 010/426] [Aptos]: Aptos NFT capabilities (#2613) --- src/Aptos/Address.h | 1 + src/Aptos/MoveTypes.h | 1 + src/Aptos/Signer.cpp | 107 ++++++++++++++++++-- src/proto/Aptos.proto | 65 +++++++++++-- tests/Aptos/SignerTests.cpp | 156 +++++++++++++++++++++++++++++- tests/interface/TWTestUtilities.h | 7 +- 6 files changed, 315 insertions(+), 22 deletions(-) diff --git a/src/Aptos/Address.h b/src/Aptos/Address.h index 52b995fa3be..45fe27aa011 100644 --- a/src/Aptos/Address.h +++ b/src/Aptos/Address.h @@ -52,6 +52,7 @@ constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcep inline const Address gAddressZero = Address("0x0"); inline const Address gAddressOne = Address("0x1"); +inline const Address gAddressThree = Address("0x3"); BCS::Serializer& operator<<(BCS::Serializer& stream, Address) noexcept; diff --git a/src/Aptos/MoveTypes.h b/src/Aptos/MoveTypes.h index d3810f300a3..7f77f9995ca 100644 --- a/src/Aptos/MoveTypes.h +++ b/src/Aptos/MoveTypes.h @@ -97,5 +97,6 @@ BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept; BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& t) noexcept; BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept; static const TypeTag gTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(gAddressOne, "aptos_coin", "AptosCoin", {})})}; +static const TypeTag gOfferNftTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(gAddressThree, "token_transfers", "offer_script", {})})}; } // namespace TW::Aptos diff --git a/src/Aptos/Signer.cpp b/src/Aptos/Signer.cpp index eb1e05424d4..e734c34edc4 100644 --- a/src/Aptos/Signer.cpp +++ b/src/Aptos/Signer.cpp @@ -11,17 +11,22 @@ #include "TransactionBuilder.h" #include "TransactionPayload.h" +namespace { +template +void serializeToArgs(std::vector& args, T&& toSerialize) { + TW::BCS::Serializer serializer; + serializer << std::forward(toSerialize); + args.emplace_back(serializer.bytes); +} +} // namespace + namespace TW::Aptos { template std::pair, nlohmann::json> commonTransferPayload(const TPayload& input) { - BCS::Serializer aSerializer; - aSerializer << Address(input.to()); std::vector args; - args.emplace_back(aSerializer.bytes); - aSerializer.clear(); - aSerializer << input.amount(); - args.emplace_back(aSerializer.bytes); + serializeToArgs(args, Address(input.to())); + serializeToArgs(args, input.amount()); nlohmann::json argsJson = nlohmann::json::array({input.to(), std::to_string(input.amount())}); return std::make_pair(args, argsJson); } @@ -35,15 +40,81 @@ TransactionPayload transferPayload(const Proto::SigningInput& input) { TransactionPayload createAccountPayload(const Proto::SigningInput& input) { ModuleId module(gAddressOne, "aptos_account"); - BCS::Serializer aSerializer; - aSerializer << Address(input.create_account().auth_key()); std::vector args; - args.emplace_back(aSerializer.bytes); + serializeToArgs(args, Address(input.create_account().auth_key())); nlohmann::json argsJson = nlohmann::json::array({input.create_account().auth_key()}); TransactionPayload payload = EntryFunction(module, "create_account", {}, args, argsJson); return payload; } +TransactionPayload claimNftPayload(const Proto::ClaimNftMessage& msg) { + std::vector args; + ModuleId module(gAddressThree, "token_transfers"); + serializeToArgs(args, Address(msg.sender())); + serializeToArgs(args, Address(msg.creator())); + serializeToArgs(args, msg.collectionname()); + serializeToArgs(args, msg.name()); + serializeToArgs(args, msg.property_version()); + // clang-format off + nlohmann::json argsJson = nlohmann::json::array( + { + msg.sender(), + msg.creator(), + msg.collectionname(), + msg.name(), + std::to_string(msg.property_version()), + }); + // clang-format on + TransactionPayload payload = EntryFunction(module, "claim_script", {}, args, argsJson); + return payload; +} + +TransactionPayload nftOfferPayload(const Proto::OfferNftMessage& msg) { + std::vector args; + ModuleId module(gAddressThree, "token_transfers"); + serializeToArgs(args, Address(msg.receiver())); + serializeToArgs(args, Address(msg.creator())); + serializeToArgs(args, msg.collectionname()); + serializeToArgs(args, msg.name()); + serializeToArgs(args, msg.property_version()); + serializeToArgs(args, msg.amount()); + // clang-format off + nlohmann::json argsJson = nlohmann::json::array( + { + msg.receiver(), + msg.creator(), + msg.collectionname(), + msg.name(), + std::to_string(msg.property_version()), + std::to_string(msg.amount()) + }); + // clang-format on + TransactionPayload payload = EntryFunction(module, "offer_script", {}, args, argsJson); + return payload; +} + +TransactionPayload cancelNftOfferPayload(const Proto::CancelOfferNftMessage& msg) { + std::vector args; + ModuleId module(gAddressThree, "token_transfers"); + serializeToArgs(args, Address(msg.receiver())); + serializeToArgs(args, Address(msg.creator())); + serializeToArgs(args, msg.collectionname()); + serializeToArgs(args, msg.name()); + serializeToArgs(args, msg.property_version()); + // clang-format off + nlohmann::json argsJson = nlohmann::json::array( + { + msg.receiver(), + msg.creator(), + msg.collectionname(), + msg.name(), + std::to_string(msg.property_version()), + }); + // clang-format on + TransactionPayload payload = EntryFunction(module, "cancel_offer_script", {}, args, argsJson); + return payload; +} + TransactionPayload tokenTransferPayload(const Proto::SigningInput& input) { auto&& [args, argsJson] = commonTransferPayload(input.token_transfer()); @@ -86,7 +157,19 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { if (!input.any_encoded().empty()) { return blindSign(input); } - auto payloadFunctor = [&input]() { + auto nftPayloadFunctor = [](const Proto::NftMessage& nftMessage) { + switch (nftMessage.nft_transaction_payload_case()) { + case Proto::NftMessage::kOfferNft: + return nftOfferPayload(nftMessage.offer_nft()); + case Proto::NftMessage::kCancelOfferNft: + return cancelNftOfferPayload(nftMessage.cancel_offer_nft()); + case Proto::NftMessage::kClaimNft: + return claimNftPayload(nftMessage.claim_nft()); + case Proto::NftMessage::NFT_TRANSACTION_PAYLOAD_NOT_SET: + throw std::runtime_error("Nft message payload not set"); + } + }; + auto payloadFunctor = [&input, &nftPayloadFunctor]() { switch (input.transaction_payload_case()) { case Proto::SigningInput::kTransfer: { return transferPayload(input); @@ -94,8 +177,12 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { case Proto::SigningInput::kTokenTransfer: { return tokenTransferPayload(input); } + case Proto::SigningInput::kNftMessage: { + return nftPayloadFunctor(input.nft_message()); + } case Proto::SigningInput::kCreateAccount: return createAccountPayload(input); + case Proto::SigningInput::TRANSACTION_PAYLOAD_NOT_SET: throw std::runtime_error("Transaction payload should be set"); } diff --git a/src/proto/Aptos.proto b/src/proto/Aptos.proto index 70d4f4eb598..4d28f034868 100644 --- a/src/proto/Aptos.proto +++ b/src/proto/Aptos.proto @@ -43,6 +43,58 @@ message CreateAccountMessage { string auth_key = 1; } +// Necessary fields to process an OfferNftMessage +message OfferNftMessage { + // Receiver address + string receiver = 1; + // Creator address + string creator = 2; + // Name of the collection + string collectionName = 3; + // Name of the NFT + string name = 4; + // Property version (should be often 0) + uint64 property_version = 5; + // Amount of NFT's to transfer (should be often 1) + uint64 amount = 6; +} + +// Necessary fields to process an CancelOfferNftMessage +message CancelOfferNftMessage { + // Receiver address + string receiver = 1; + // Creator address + string creator = 2; + // Name of the collection + string collectionName = 3; + // Name of the NFT + string name = 4; + // Property version (should be often 0) + uint64 property_version = 5; +} + +// Necessary fields to process an ClaimNftMessage +message ClaimNftMessage { + // Sender address + string sender = 1; + // Creator address + string creator = 2; + // Name of the collection + string collectionName = 3; + // Name of the NFT + string name = 4; + // Property version (should be often 0) + uint64 property_version = 5; +} + +message NftMessage { + oneof nft_transaction_payload { + OfferNftMessage offer_nft = 1; + CancelOfferNftMessage cancel_offer_nft = 2; + ClaimNftMessage claim_nft = 3; + } +} + // Input data necessary to create a signed transaction. message SigningInput { // Sender Account address (string) @@ -53,19 +105,20 @@ message SigningInput { TransferMessage transfer = 3; TokenTransferMessage token_transfer = 4; CreateAccountMessage create_account = 5; + NftMessage nft_message = 6; } // Max gas amount that the user is willing to pay (uint64) - uint64 max_gas_amount = 6; + uint64 max_gas_amount = 7; // Gas unit price - queried through API (uint64) - uint64 gas_unit_price = 7; + uint64 gas_unit_price = 8; // Expiration timestamp for the transaction, can't be in the past (uint64) - uint64 expiration_timestamp_secs = 8; + uint64 expiration_timestamp_secs = 9; // Chain id 1 (mainnet) 32(devnet) (uint32 - casted in uint8_t later) - uint32 chain_id = 9; + uint32 chain_id = 10; // Private key to sign the transaction (bytes) - bytes private_key = 10; + bytes private_key = 11; // hex encoded function to sign, use it for smart contract approval (string) - string any_encoded = 11; + string any_encoded = 12; } // Information related to the signed transaction diff --git a/tests/Aptos/SignerTests.cpp b/tests/Aptos/SignerTests.cpp index ece577f4e0b..1ebb97cfcc3 100644 --- a/tests/Aptos/SignerTests.cpp +++ b/tests/Aptos/SignerTests.cpp @@ -9,6 +9,7 @@ #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" +#include "../interface/TWTestUtilities.h" #include #include @@ -53,7 +54,152 @@ TEST(AptosSigner, DummyTxSign) { } )"_json; nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - ASSERT_EQ(expectedJson, parsedJson); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, ClaimNftTxSign) { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x60b51e15140ec0b7650334e948fb447ce3cb13ae63492260461ebfa9d02e85c4?network=testnet + Proto::SigningInput input; + input.set_sender("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(19); + auto& tf = *input.mutable_nft_message()->mutable_claim_nft(); + tf.set_sender("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); + tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); + tf.set_collectionname("Topaz Troopers"); + tf.set_name("Topaz Trooper #20068"); + tf.set_property_version(0); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(2); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002"); + ASSERT_EQ(hex(result.authenticator().signature()), "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::claim_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "19", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, NftOfferTxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x514e473618bd3cb89a2b110b7c473db9a2e10532f98eb42d02d86fb31c00525d?network=testnet + Proto::SigningInput input; + input.set_sender("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); + input.set_sequence_number(1); + auto& tf = *input.mutable_nft_message()->mutable_offer_nft(); + tf.set_receiver("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); + tf.set_collectionname("Topaz Troopers"); + tf.set_name("Topaz Trooper #20068"); + tf.set_property_version(0); + tf.set_amount(1); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(2); + auto privateKey = PrivateKey(parse_hex("7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002"); + ASSERT_EQ(hex(result.authenticator().signature()), "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f"); + ASSERT_EQ(hex(result.encoded()), "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0", "1"], + "function": "0x3::token_transfers::offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "sequence_number": "1", + "signature": { + "public_key": "0xd1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a4113", + "signature": "0xaf5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, CancelNftOfferTxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x0b8c64e6847c368e4c6bd2cce0e9eab378971b0ef2e3bc40cbd292910a80201d?network=testnet + Proto::SigningInput input; + input.set_sender("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(21); + auto& tf = *input.mutable_nft_message()->mutable_cancel_offer_nft(); + tf.set_receiver("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); + tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); + tf.set_collectionname("Topaz Troopers"); + tf.set_name("Topaz Trooper #20068"); + tf.set_property_version(0); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(2); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002"); + ASSERT_EQ(hex(result.authenticator().signature()), "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::cancel_offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "21", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); } TEST(AptosSigner, TxSign) { @@ -95,7 +241,7 @@ TEST(AptosSigner, TxSign) { } )"_json; nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - ASSERT_EQ(expectedJson, parsedJson); + assertJSONEqual(expectedJson, parsedJson); } TEST(AptosSigner, CreateAccount) { @@ -136,7 +282,7 @@ TEST(AptosSigner, CreateAccount) { } )"_json; nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - ASSERT_EQ(expectedJson, parsedJson); + assertJSONEqual(expectedJson, parsedJson); } TEST(AptosSigner, BlindSign) { @@ -180,7 +326,7 @@ TEST(AptosSigner, BlindSign) { } )"_json; nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - ASSERT_EQ(expectedJson, parsedJson); + assertJSONEqual(expectedJson, parsedJson); } TEST(AptosSigner, TokenTxSign) { @@ -225,7 +371,7 @@ TEST(AptosSigner, TokenTxSign) { } )"_json; nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - ASSERT_EQ(expectedJson, parsedJson); + assertJSONEqual(expectedJson, parsedJson); } } // namespace TW::Aptos::tests diff --git a/tests/interface/TWTestUtilities.h b/tests/interface/TWTestUtilities.h index a03691dc7e4..78c0d89cb39 100644 --- a/tests/interface/TWTestUtilities.h +++ b/tests/interface/TWTestUtilities.h @@ -30,10 +30,15 @@ inline void assertHexEqual(const std::shared_ptr& data, const char* expe assertStringsEqual(hex, expected); } + +inline void assertJSONEqual(const nlohmann::json& lhs, const nlohmann::json& rhs) { + ASSERT_EQ(lhs, rhs); +} + inline void assertJSONEqual(const std::string& lhs, const char* expected) { auto lhsJson = nlohmann::json::parse(lhs); auto rhsJson = nlohmann::json::parse(std::string(expected)); - ASSERT_EQ(lhsJson, rhsJson); + return assertJSONEqual(lhsJson, rhsJson); } inline std::vector* dataFromTWData(TWData* data) { From 817798f7f67e055ece3db4aac85d483d23b80ee6 Mon Sep 17 00:00:00 2001 From: Klimov Sergey Date: Mon, 10 Oct 2022 07:34:18 +0300 Subject: [PATCH 011/426] Update Extrinsic.cpp (#2625) --- src/Polkadot/Extrinsic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Polkadot/Extrinsic.cpp b/src/Polkadot/Extrinsic.cpp index 6ce75c13c8d..8cab2fb5cef 100644 --- a/src/Polkadot/Extrinsic.cpp +++ b/src/Polkadot/Extrinsic.cpp @@ -17,7 +17,7 @@ static constexpr uint32_t multiAddrSpecVersion = 28; static constexpr uint32_t multiAddrSpecVersionKsm = 2028; static const std::string balanceTransfer = "Balances.transfer"; -static const std::string utilityBatch = "Utility.batch"; +static const std::string utilityBatch = "Utility.batch_all"; static const std::string stakingBond = "Staking.bond"; static const std::string stakingBondExtra = "Staking.bond_extra"; static const std::string stakingUnbond = "Staking.unbond"; From 637fae2a08266ac2bfd2c87cb7610723c6c63656 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Mon, 10 Oct 2022 06:34:48 +0200 Subject: [PATCH 012/426] Bitcoin Message Signing and Verification (#2623) --- .../TrustWalletCore/TWBitcoinMessageSigner.h | 43 +++++ src/Bitcoin/MessageSigner.cpp | 111 ++++++++++++ src/Bitcoin/MessageSigner.h | 61 +++++++ src/PublicKey.cpp | 32 +++- src/PublicKey.h | 19 +- src/interface/TWBitcoinMessageSigner.cpp | 22 +++ ...TWBitcoin.cpp => TWBitcoinSigHashType.cpp} | 0 swift/Tests/Blockchains/BitcoinTests.swift | 9 + tests/Bitcoin/MessageSignerTests.cpp | 167 ++++++++++++++++++ tests/PublicKeyTests.cpp | 52 ++++++ 10 files changed, 506 insertions(+), 10 deletions(-) create mode 100644 include/TrustWalletCore/TWBitcoinMessageSigner.h create mode 100644 src/Bitcoin/MessageSigner.cpp create mode 100644 src/Bitcoin/MessageSigner.h create mode 100644 src/interface/TWBitcoinMessageSigner.cpp rename src/interface/{TWBitcoin.cpp => TWBitcoinSigHashType.cpp} (100%) create mode 100644 tests/Bitcoin/MessageSignerTests.cpp diff --git a/include/TrustWalletCore/TWBitcoinMessageSigner.h b/include/TrustWalletCore/TWBitcoinMessageSigner.h new file mode 100644 index 00000000000..d0fcd2e17bd --- /dev/null +++ b/include/TrustWalletCore/TWBitcoinMessageSigner.h @@ -0,0 +1,43 @@ +// Copyright © 2017-2022 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" +#include "TWPrivateKey.h" + +TW_EXTERN_C_BEGIN + +/// Bitcoin message signing and verification. +/// +/// Bitcoin Core and some other wallets support a message signing & verification format, to create a proof (a signature) +/// that someone has access to the private keys of a specific address. +/// This feature currently works on old legacy addresses only. +TW_EXPORT_CLASS +struct TWBitcoinMessageSigner; + +/// Sign a message. +/// +/// \param privateKey: the private key used for signing +/// \param address: the address that matches the privateKey, must be a legacy address (P2PKH) +/// \param message: A custom message which is input to the signing. +/// \note Address is derived assuming compressed public key format. +/// \returns the signature, Base64-encoded. On invalid input empty string is returned. Returned object needs to be deleteed after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWBitcoinMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull address, TWString* _Nonnull message); + +/// Verify signature for a message. +/// +/// \param address: address to use, only legacy is supported +/// \param message: the message signed (without prefix) +/// \param signature: in Base64-encoded form. +/// \returns false on any invalid input (does not throw). +TW_EXPORT_STATIC_METHOD +bool TWBitcoinMessageSignerVerifyMessage(TWString* _Nonnull address, TWString* _Nonnull message, TWString* _Nonnull signature); + +TW_EXTERN_C_END diff --git a/src/Bitcoin/MessageSigner.cpp b/src/Bitcoin/MessageSigner.cpp new file mode 100644 index 00000000000..21f3ca90de7 --- /dev/null +++ b/src/Bitcoin/MessageSigner.cpp @@ -0,0 +1,111 @@ +// Copyright © 2017-2022 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 "MessageSigner.h" +#include "Address.h" + +#include "Base64.h" +#include "BinaryCoding.h" +#include "Coin.h" +#include "Data.h" +#include "HexCoding.h" + +using namespace TW; + +namespace TW::Bitcoin { + +// lenght-encode a message string +Data messageToData(const std::string& message) { + Data d; + TW::encodeVarInt(message.size(), d); + TW::append(d, TW::data(message)); + return d; +} + +// append prefix and length-encode message string +Data messageToFullData(const std::string& message) { + Data d = messageToData(MessageSigner::MessagePrefix); + TW::append(d, messageToData(message)); + return d; +} + +Data MessageSigner::messageToHash(const std::string& message) { + Data d = messageToFullData(message); + return Hash::sha256d(d.data(), d.size()); +} + +std::string MessageSigner::signMessage(const PrivateKey& privateKey, const std::string& address, const std::string& message, bool compressed) { + if (!Address::isValid(address)) { + throw std::invalid_argument("Address is not valid (legacy) address"); + } + std::string addrFromKey; + if (compressed) { + const auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + addrFromKey = Address(pubKey, TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); + } else { + const auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + const auto keyHash = pubKey.hash(Data{TW::p2pkhPrefix(TWCoinTypeBitcoin)}, Hash::HasherSha256ripemd); + addrFromKey = Address(keyHash).string(); + } + if (addrFromKey != address) { + throw std::invalid_argument("Address does not match key"); + } + const auto messageHash = messageToHash(message); + const auto signature = privateKey.sign(messageHash, TWCurveSECP256k1); + + // The V value: add 31 (or 27 for compressed), and move to the first byte + const byte v = signature[SignatureRSLength] + PublicKey::SignatureVOffset + (compressed ? 4ul : 0ul); + auto sigAdjusted = Data{v}; + TW::append(sigAdjusted, TW::subData(signature, 0, SignatureRSLength)); + return Base64::encode(sigAdjusted); +} + +std::string MessageSigner::recoverAddressFromMessage(const std::string& message, const Data& signature) { + if (signature.size() < SignatureRSVLength) { + throw std::invalid_argument("signature too short"); + } + const auto messageHash = MessageSigner::messageToHash(message); + auto recId = signature[0]; + auto compressed = false; + if (recId >= PublicKey::SignatureVOffset + 4) { + recId -= 4; + compressed = true; + } + if (recId >= PublicKey::SignatureVOffset) { + recId -= PublicKey::SignatureVOffset; + } + + const auto publicKeyRecovered = PublicKey::recoverRaw(TW::subData(signature, 1), recId, messageHash); + + if (!compressed) { + // uncompressed public key + const auto keyHash = publicKeyRecovered.hash(Data{TW::p2pkhPrefix(TWCoinTypeBitcoin)}, Hash::HasherSha256ripemd); + return Bitcoin::Address(keyHash).string(); + } + // compressed + const auto publicKeyRecoveredCompressed = publicKeyRecovered.compressed(); + return Bitcoin::Address(publicKeyRecoveredCompressed, TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); +} + +bool MessageSigner::verifyMessage(const std::string& address, const std::string& message, const std::string& signature) noexcept { + try { + const auto signatureData = Base64::decode(signature); + return verifyMessage(address, message, signatureData); + } catch (...) { + return false; + } +} + +/// May throw +bool MessageSigner::verifyMessage(const std::string& address, const std::string& message, const Data& signature) { + if (!Bitcoin::Address::isValid(address)) { + throw std::invalid_argument("Input address invalid, must be valid legacy"); + } + const auto addressRecovered = recoverAddressFromMessage(message, signature); + return (addressRecovered == address); +} + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/MessageSigner.h b/src/Bitcoin/MessageSigner.h new file mode 100644 index 00000000000..6779bfaf52d --- /dev/null +++ b/src/Bitcoin/MessageSigner.h @@ -0,0 +1,61 @@ +// Copyright © 2017-2022 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 "PrivateKey.h" + +#include + +namespace TW::Bitcoin { + +/// Class for message signing and verification. +/// +/// Bitcoin Core and some other wallets support a message signing & verification format, to create a proof (a signature) +/// that someone has access to the private keys of a specific address. +/// This feature currently works on old legacy addresses only. +class MessageSigner { + public: + /// Sign a message. + /// privateKey: the private key used for signing + /// address: the address that matches the privateKey, must be a legacy address (P2PKH) + /// message: A custom message which is input to the signing. + /// compressed: True by default, as addresses are generated from the hash of the compressed public key. + /// However, in some instances key hash is generated from the hash of the extended public key, + /// that's also supported here as well for compatibility. + /// Returns the signature, Base64-encoded. + /// Throws on invalid input. + static std::string signMessage(const PrivateKey& privateKey, const std::string& address, const std::string& message, bool compressed = true); + + /// Verify signature for a message. + /// address: address to use, only legacy is supported + /// message: the message signed (without prefix) + /// signature: in Base64-encoded form. + /// Returns false on any invalid input (does not throw). + static bool verifyMessage(const std::string& address, const std::string& message, const std::string& signature) noexcept; + + /// Verify signature for a message. + /// Address: address to use, only legacy is supported + /// message: the message signed (without prefix) + /// signature: in binary form. + /// May throw + static bool verifyMessage(const std::string& address, const std::string& message, const Data& signature); + + /// Recover address from signature and message. May throw. + static std::string recoverAddressFromMessage(const std::string& message, const Data& signature); + + /// Append prefix and compute hash for a message + static Data messageToHash(const std::string& message); + + static constexpr auto MessagePrefix = "Bitcoin Signed Message:\n"; + static const byte DigestLength = 32; + static const byte SignatureRSLength = 64; + static constexpr byte SignatureRSVLength = SignatureRSLength + 1; + static const byte VOffset = 27; +}; + +} // namespace TW::Bitcoin diff --git a/src/PublicKey.cpp b/src/PublicKey.cpp index 10c2e59d88a..0fcd53c3a3b 100644 --- a/src/PublicKey.cpp +++ b/src/PublicKey.cpp @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #include "PublicKey.h" +#include "PrivateKey.h" #include "Data.h" #include @@ -206,22 +207,35 @@ Data PublicKey::hash(const Data& prefix, Hash::Hasher hasher, bool skipTypeByte) return result; } -PublicKey PublicKey::recover(const Data& signature, const Data& message) { - if (signature.size() < 65) { +PublicKey PublicKey::recoverRaw(const Data& signatureRS, byte recId, const Data& messageDigest) { + if (signatureRS.size() < 2 * PrivateKey::_size) { throw std::invalid_argument("signature too short"); } - auto v = signature[64]; - // handle EIP155 Eth encoding of V, of the form 27+v, or 35+chainID*2+v - if (v >= 27) { - v = !(v & 0x01); + if (recId >= 4) { + throw std::invalid_argument("Invalid recId (>=4)"); + } + if (messageDigest.size() < PrivateKey::_size) { + throw std::invalid_argument("digest too short"); } - TW::Data result(65); - if (ecdsa_recover_pub_from_sig(&secp256k1, result.data(), signature.data(), message.data(), v) != 0) { - throw std::invalid_argument("recover failed"); + TW::Data result(secp256k1SignatureSize); + if (auto ret = ecdsa_recover_pub_from_sig(&secp256k1, result.data(), signatureRS.data(), messageDigest.data(), recId); ret != 0) { + throw std::invalid_argument("recover failed " + std::to_string(ret)); } return PublicKey(result, TWPublicKeyTypeSECP256k1Extended); } +PublicKey PublicKey::recover(const Data& signature, const Data& messageDigest) { + if (signature.size() < secp256k1SignatureSize) { + throw std::invalid_argument("signature too short"); + } + auto v = signature[secp256k1SignatureSize - 1]; + // handle EIP155 Eth encoding of V, of the form 27+v, or 35+chainID*2+v + if (v >= PublicKey::SignatureVOffset) { + v = !(v & 0x01); + } + return recoverRaw(signature, v, messageDigest); +} + bool PublicKey::isValidED25519() const { if (type != TWPublicKeyTypeED25519) { return false; diff --git a/src/PublicKey.h b/src/PublicKey.h index 3b4691cac35..111f0fdf306 100644 --- a/src/PublicKey.h +++ b/src/PublicKey.h @@ -30,6 +30,12 @@ class PublicKey { /// The number of bytes in a secp256k1 and nist256p1 extended public key. static const size_t secp256k1ExtendedSize = 65; + /// The number of bytes in a secp256k1 signature. + static const size_t secp256k1SignatureSize = 65; + + /// Magic number used in V compnent encoding + static const byte SignatureVOffset = 27; + /// The public key bytes. Data bytes; @@ -74,8 +80,19 @@ class PublicKey { /// bytes and then prepending the prefix. Data hash(const Data& prefix, Hash::Hasher hasher = Hash::HasherSha256ripemd, bool skipTypeByte = false) const; + /// Recover public key (SECP256k1Extended) from signature R, S, V values + /// signatureRS: 2x32 bytes with the R and S values + /// recId: the recovery ID, a.k.a. V value, 0 <= v < 4 + /// messageDigest: message digest (hash) to be signed + /// Throws on invalid data. + static PublicKey recoverRaw(const Data& signatureRS, byte recId, const Data& messageDigest); + /// Recover public key from signature (SECP256k1Extended) - static PublicKey recover(const Data& signature, const Data& message); + /// signature: 65-byte signature (R, S, and V). V can have higher value bits, as used by Ethereum (for values over 27 the negated last bit is taken). + /// messageDigest: message digest (hash) to be signed + /// Throws on invalid data. + /// Naming is kept for backwards compatibility. + static PublicKey recover(const Data& signature, const Data& messageDigest); /// Check if this key makes a valid ED25519 key (it is on the curve) bool isValidED25519() const; diff --git a/src/interface/TWBitcoinMessageSigner.cpp b/src/interface/TWBitcoinMessageSigner.cpp new file mode 100644 index 00000000000..d5b0985fbb1 --- /dev/null +++ b/src/interface/TWBitcoinMessageSigner.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 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 + +#include "Bitcoin/MessageSigner.h" + +TWString* _Nonnull TWBitcoinMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull address, TWString* _Nonnull message) { + try { + const auto signature = TW::Bitcoin::MessageSigner::signMessage(privateKey->impl, TWStringUTF8Bytes(address), TWStringUTF8Bytes(message), true); + return TWStringCreateWithUTF8Bytes(signature.c_str()); + } catch (...) { + return TWStringCreateWithUTF8Bytes(""); + } +} + +bool TWBitcoinMessageSignerVerifyMessage(TWString* _Nonnull address, TWString* _Nonnull message, TWString* _Nonnull signature) { + return TW::Bitcoin::MessageSigner::verifyMessage(TWStringUTF8Bytes(address), TWStringUTF8Bytes(message), TWStringUTF8Bytes(signature)); +} diff --git a/src/interface/TWBitcoin.cpp b/src/interface/TWBitcoinSigHashType.cpp similarity index 100% rename from src/interface/TWBitcoin.cpp rename to src/interface/TWBitcoinSigHashType.cpp diff --git a/swift/Tests/Blockchains/BitcoinTests.swift b/swift/Tests/Blockchains/BitcoinTests.swift index e4b7255fc76..4ace92e08e5 100644 --- a/swift/Tests/Blockchains/BitcoinTests.swift +++ b/swift/Tests/Blockchains/BitcoinTests.swift @@ -174,4 +174,13 @@ class BitcoinTransactionSignerTests: XCTestCase { XCTAssertEqual(output.error, .ok) XCTAssertEqual(output.encoded.hexString, "01000000026c90312e53a3411347a197bfd637c2583d617dd2317262a70e1b5245d2f1e36a000000008a47304402201a631068ea5ddea19467ef7c932a0f3b04f366ca2beaf70e18958e47456124980220614816c449e39cf6acc6625e1cf3100db1db7c0b755bdbb6804d4fa3c4b735d10141041b3937fac1f14074447cde9d3a324ed292d2865ed0d7a7da26cb43558ce4db4ef33c47e820e53031ae16bb0c39205def059a5ca8e1d617650eabc72c5206a81dffffffff13bf27945c669cf3c1d70cf3048f4ab14f1ab6acf06d10d425e8288217a81efd000000008a473044022051d381d8f48a9a4866ca4109f12647922514604a4733e8da8aac046e19275f700220797c3ebf20df7d2a9fed283f9d0ad14cbd656cafb5ec70a2b1c85646ea7485190141041b3937fac1f14074447cde9d3a324ed292d2865ed0d7a7da26cb43558ce4db4ef33c47e820e53031ae16bb0c39205def059a5ca8e1d617650eabc72c5206a81dffffffff0194590000000000001976a914a0c0a50f986924e65ae9bd18eafae448f83117ed88ac00000000") } + + func testBitcoinMessageSigner() { + let verifyResult = BitcoinMessageSigner.verifyMessage( + address: "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", + message: "test signature", + signature: "H+3L5IbSVcejp4S2VwLXCxLEMQAWDvKbE8lQyq0ocdvyM1aoEudkzN/S/qLI3vnNOFY6V13BXWSFrPr3OjGa5Dk=" + ) + XCTAssertTrue(verifyResult) + } } diff --git a/tests/Bitcoin/MessageSignerTests.cpp b/tests/Bitcoin/MessageSignerTests.cpp new file mode 100644 index 00000000000..e8df4e56631 --- /dev/null +++ b/tests/Bitcoin/MessageSignerTests.cpp @@ -0,0 +1,167 @@ +// Copyright © 2017-2022 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 "Bitcoin/MessageSigner.h" +#include +#include "Bitcoin/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "Base64.h" +#include "Coin.h" +#include "Data.h" + +#include +#include + +#include +#include +#include // TODO remove + +namespace TW::Bitcoin::MessageSignerTests { + +const auto gPrivateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + +TEST(BitcoinMessageSigner, VerifyMessage) { + EXPECT_TRUE(MessageSigner::verifyMessage( + "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", + "test signature", + "H+3L5IbSVcejp4S2VwLXCxLEMQAWDvKbE8lQyq0ocdvyM1aoEudkzN/S/qLI3vnNOFY6V13BXWSFrPr3OjGa5Dk=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "This is an example of a signed message.", + "G39Qf0XrZHICWbz3r5gOkcgTRw3vM4leGjiR3refr/K1OezcKmmXaLn4zc8ji2rjbBUIMrIhH/jc5Z2qEEz7qVk=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1H8X4u6CVZRTLLNbUQTKAnc5vCkqWMpwfF", + "compressed key", + "IKUI9v2xbHogJe8HKXI2M5KEhMKaW6fjNxtyEy27Mf+3/e1ht4jZoc85e4F8stPsxt4Xcg8Yr42S28O6L/Qx9fE=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + "test signature", + "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + "another text", + "H7vrF2C+TlFiHyegAw3QLv6SK0myuEEXUOgfx0+Qio1YVDuSa6p/OHpoQVlUt3F8QJdbdZN9M1h/fYEAnEz16V0=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1E4T9JZ3mq6cdgiRJEWzHqDXb9t322fE6d", + "test signature", + "HLH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo=" + )); +} + +TEST(BitcoinMessageSigner, SignAndVerify) { + const auto pubKey = gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(pubKey.bytes), "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); + const auto address = Address(pubKey, TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); + EXPECT_EQ(address, "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + + { + const auto msg = "test signature"; + const auto signature = MessageSigner::signMessage(gPrivateKey, address, msg); + EXPECT_EQ(signature, "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(MessageSigner::verifyMessage(address, msg, signature)); + } + { + const auto msg = "another text"; + const auto signature = MessageSigner::signMessage(gPrivateKey, address, msg); + EXPECT_EQ(signature, "H7vrF2C+TlFiHyegAw3QLv6SK0myuEEXUOgfx0+Qio1YVDuSa6p/OHpoQVlUt3F8QJdbdZN9M1h/fYEAnEz16V0="); + + EXPECT_TRUE(MessageSigner::verifyMessage(address, msg, signature)); + } + + // uncompressed + const auto pubKeyUncomp = gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + const auto keyHash = pubKeyUncomp.hash(Data{TW::p2pkhPrefix(TWCoinTypeBitcoin)}, Hash::HasherSha256ripemd); + const auto addressUncomp = Address(keyHash).string(); + EXPECT_EQ(addressUncomp, "1E4T9JZ3mq6cdgiRJEWzHqDXb9t322fE6d"); + { + const auto msg = "test signature"; + const auto signature = MessageSigner::signMessage(gPrivateKey, addressUncomp, msg, false); + EXPECT_EQ(signature, "HLH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(MessageSigner::verifyMessage(addressUncomp, msg, signature)); + } +} + +TEST(BitcoinMessageSigner, SignNegative) { + const auto address = Address(gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1), TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); + EXPECT_EQ(address, "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + const auto msg = "test signature"; + // Use invalid address + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "__THIS_IS_NOT_A_VALID_ADDRESS__", msg), "Address is not valid (legacy) address"); + // Use invalid address, not legacy + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", msg), "Address is not valid (legacy) address"); + // Use valid, but not matching key + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", msg), "Address does not match key"); +} + +TEST(BitcoinMessageSigner, VerifyNegative) { + const auto sig = parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae439"); + // Baseline positive + EXPECT_TRUE(MessageSigner::verifyMessage( + "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", + "test signature", + sig + )); + + // Provide non-matching address + EXPECT_FALSE(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "test signature", + sig + )); + // Signature too short + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "signature too short"); + // Invalid address + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "__THIS_IS_NOT_A_VALID_ADDRESS__", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "Input address invalid"); + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "Input address invalid"); +} + +TEST(BitcoinMessageSigner, MessageToHash) { + EXPECT_EQ(hex(MessageSigner::messageToHash("Hello, world!")), "02d6c0643e40b0db549cbbd7eb47dcab71a59d7017199ebde6b272f28fbbf95f"); + EXPECT_EQ(hex(MessageSigner::messageToHash("test signature")), "8e81cc5bca9862d8b7f22be1f7cb762b49121cf4e1611c27906a041f9a9eb21f"); +} + +TEST(TWBitcoinMessageSigner, VerifyMessage) { + EXPECT_TRUE(TWBitcoinMessageSignerVerifyMessage( + STRING("1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr").get(), + STRING("test signature").get(), + STRING("H+3L5IbSVcejp4S2VwLXCxLEMQAWDvKbE8lQyq0ocdvyM1aoEudkzN/S/qLI3vnNOFY6V13BXWSFrPr3OjGa5Dk=").get() + )); +} + +TEST(TWBitcoinMessageSigner, SignAndVerify) { + const auto privKeyData = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto address = STRING("19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + const auto message = STRING("test signature"); + + const auto signature = WRAPS(TWBitcoinMessageSignerSignMessage(privateKey.get(), address.get(), message.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(TWBitcoinMessageSignerVerifyMessage(address.get(), message.get(), signature.get())); +} + +} // namespace TW::Bitcoin diff --git a/tests/PublicKeyTests.cpp b/tests/PublicKeyTests.cpp index 1154e3d8d6f..a9e2f09d115 100644 --- a/tests/PublicKeyTests.cpp +++ b/tests/PublicKeyTests.cpp @@ -250,6 +250,58 @@ TEST(PublicKeyTests, VerifySchnorrWrongType) { EXPECT_FALSE(publicKey.verifyZilliqa(signature, digest)); } +TEST(PublicKeyTests, RecoverRaw) { + { + const auto message = parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"); + const auto signature = parse_hex("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); + { + const auto publicKey = PublicKey::recoverRaw(signature, 1ul, message); + EXPECT_EQ(publicKey.type, TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(hex(publicKey.bytes), "0456d8089137b1fd0d890f8c7d4a04d0fd4520a30b19518ee87bd168ea12ed8090329274c4c6c0d9df04515776f2741eeffc30235d596065d718c3973e19711ad0"); + } + { // same data but different recId -> different result + const auto publicKey = PublicKey::recoverRaw(signature, 0ul, message); + EXPECT_EQ(publicKey.type, TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(hex(publicKey.bytes), "043fc5bf5fec35b6ffe6fd246226d312742a8c296bfa57dd22da509a2e348529b7ddb9faf8afe1ecda3c05e7b2bda47ee1f5a87e952742b22afca560b29d972fcf"); + } + } + { + const auto message = parse_hex("6468eb103d51c9a683b51818fdb73390151c9973831d2cfb4e9587ad54273155"); + const auto signature = parse_hex("92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c646487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"); + const auto recovered = PublicKey::recoverRaw(signature, 0ul, message); + EXPECT_EQ(hex(recovered.bytes), "0463ade8ebc212b85e7e4278dc3dcb4f9cc18aab912ef5d302b5d1940e772e9e1a9213522efddad487bbd5dd7907e8e776f918e9a5e4cb51893724e9fe76792a4f"); + } +} + +TEST(PublicKeyTests, SignAndRecoverRaw) { + const auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(hex(publicKey.bytes), "0463ade8ebc212b85e7e4278dc3dcb4f9cc18aab912ef5d302b5d1940e772e9e1a9213522efddad487bbd5dd7907e8e776f918e9a5e4cb51893724e9fe76792a4f"); + const auto message = parse_hex("6468eb103d51c9a683b51818fdb73390151c9973831d2cfb4e9587ad54273155"); + + // sign + const auto signature = privateKey.sign(message, TWCurveSECP256k1); + EXPECT_EQ(hex(signature), "92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c646487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e5800"); + + // revocer + const auto pubkeyRecovered = PublicKey::recoverRaw(signature, signature[64], message); + EXPECT_EQ(hex(pubkeyRecovered.bytes), hex(publicKey.bytes)); + EXPECT_EQ(hex(pubkeyRecovered.bytes), "0463ade8ebc212b85e7e4278dc3dcb4f9cc18aab912ef5d302b5d1940e772e9e1a9213522efddad487bbd5dd7907e8e776f918e9a5e4cb51893724e9fe76792a4f"); +} + +TEST(PublicKeyTests, RecoverRawNegative) { + const auto message = parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"); + const auto signature = parse_hex("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); + // recid >= 4 + EXPECT_EXCEPTION(PublicKey::recoverRaw(signature, 4ul, message), "Invalid recId"); + // signature too short + EXPECT_EXCEPTION(PublicKey::recoverRaw(parse_hex("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd"), 1ul, message), + "signature too short"); + // Digest too short + EXPECT_EXCEPTION(PublicKey::recoverRaw(signature, 1ul, parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb114")), + "digest too short"); +} + TEST(PublicKeyTests, Recover) { { const auto message = parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"); From ff9e87007b1448a25f886cc1ebe43ed884d586f3 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 10 Oct 2022 11:52:20 +0200 Subject: [PATCH 013/426] [Misc]: Update Package.swift to 3.0.6 targets #2630 --- Package.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 7da39e00a9d..7f6216bde07 100644 --- a/Package.swift +++ b/Package.swift @@ -12,13 +12,13 @@ let package = Package( targets: [ .binaryTarget( name: "WalletCore", - url: "https://github.com/trustwallet/wallet-core/releases/download/2.9.5/WalletCore.xcframework.zip", - checksum: "bb3bccd29e4f03c58bfa1fd746a0f8d6fc4ffc9ec2183beb4facd7b01974a9a2" + url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.6/WalletCore.xcframework.zip", + checksum: "a3df0c2b30fc59ede0a2600266fc19b8c0cf655dbef3fb832488c8ddedcb6b93" ), .binaryTarget( name: "SwiftProtobuf", - url: "https://github.com/trustwallet/wallet-core/releases/download/2.9.5/SwiftProtobuf.xcframework.zip", - checksum: "7903f5e9487db4764dc57be000c384a0619a96c275993711f3a5a858d3c865bd" + url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.6/SwiftProtobuf.xcframework.zip", + checksum: "61fa8483d4bd43f1898db6997eff0279426f15f9e518e12db0d762ec5f927a9b" ) ] ) From 3d1aaf076fb6f783a5b84b2aa30c24c4e5d7d6d0 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Tue, 11 Oct 2022 17:11:07 +0200 Subject: [PATCH 014/426] [TestOnly] `tests` folder reorg (#2627) --- codegen/bin/newcoin | 8 ++-- codegen/lib/coin_test_gen.rb | 2 +- codegen/lib/templates/TWCoinTypeTests.cpp.erb | 2 +- .../templates/newcoin/TWAddressTests.cpp.erb | 2 +- .../templates/newcoin/TWSignerTests.cpp.erb | 2 +- swift/Tests/Blockchains/Data | 2 +- tests/CMakeLists.txt | 1 + tests/{ => chains}/Aeternity/AddressTests.cpp | 0 tests/{ => chains}/Aeternity/SignerTests.cpp | 0 .../Aeternity/TWAeternityAddressTests.cpp | 2 +- .../Aeternity/TWAnySignerTests.cpp | 2 +- .../Aeternity/TWCoinTypeTests.cpp | 2 +- .../Aeternity/TransactionTests.cpp | 2 +- tests/{ => chains}/Aion/AddressTests.cpp | 0 tests/{ => chains}/Aion/RLPTests.cpp | 0 tests/{ => chains}/Aion/SignerTests.cpp | 0 tests/{ => chains}/Aion/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Aion/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Aion/TransactionTests.cpp | 0 tests/{ => chains}/Algorand/AddressTests.cpp | 0 tests/{ => chains}/Algorand/SignerTests.cpp | 0 .../Algorand/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Algorand/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Aptos/AddressTests.cpp | 0 tests/{ => chains}/Aptos/MoveTypesTests.cpp | 0 tests/{ => chains}/Aptos/SignerTests.cpp | 2 +- tests/{ => chains}/Aptos/TWAnySignerTests.cpp | 2 +- .../Aptos/TWAptosAddressTests.cpp | 2 +- tests/{ => chains}/Aptos/TWCoinTypeTests.cpp | 2 +- .../Aptos/TransactionPayloadTests.cpp | 0 .../{ => chains}/Arbitrum/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Aurora/TWCoinTypeTests.cpp | 2 +- .../Avalanche/TWCoinTypeTests.cpp | 2 +- .../BandChain/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Binance/SignerTests.cpp | 0 .../{ => chains}/Binance/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Binance/TWCoinTypeTests.cpp | 2 +- .../BinanceSmartChain/SignerTests.cpp | 2 +- .../BinanceSmartChain/TWAnyAddressTests.cpp | 2 +- .../BinanceSmartChain/TWCoinTypeTests.cpp | 2 +- .../Bitcoin/BitcoinAddressTests.cpp | 0 .../Bitcoin/BitcoinScriptTests.cpp | 2 +- .../Bitcoin/FeeCalculatorTests.cpp | 0 .../Bitcoin/InputSelectorTests.cpp | 0 .../Bitcoin/MessageSignerTests.cpp | 1 - .../Bitcoin/SegwitAddressTests.cpp | 0 .../Bitcoin/TWBitcoinAddressTests.cpp | 2 +- .../Bitcoin/TWBitcoinScriptTests.cpp | 2 +- .../Bitcoin/TWBitcoinSigningTests.cpp | 0 .../Bitcoin/TWBitcoinTransactionTests.cpp | 0 .../{ => chains}/Bitcoin/TWCoinTypeTests.cpp | 2 +- .../Bitcoin/TWSegwitAddressTests.cpp | 2 +- .../Bitcoin/TransactionPlanTests.cpp | 0 .../Bitcoin/TxComparisonHelper.cpp | 0 .../{ => chains}/Bitcoin/TxComparisonHelper.h | 0 .../BitcoinCash/TWBitcoinCashTests.cpp | 2 +- .../BitcoinCash/TWCoinTypeTests.cpp | 2 +- .../BitcoinGold/TWAddressTests.cpp | 2 +- .../BitcoinGold/TWBitcoinGoldTests.cpp | 2 +- .../BitcoinGold/TWCoinTypeTests.cpp | 2 +- .../BitcoinGold/TWSegwitAddressTests.cpp | 2 +- .../BitcoinGold/TWSignerTests.cpp | 2 +- .../{ => chains}/Bluzelle/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Boba/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Callisto/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Cardano/AddressTests.cpp | 0 tests/{ => chains}/Cardano/SigningTests.cpp | 2 +- tests/{ => chains}/Cardano/StakingTests.cpp | 2 +- .../Cardano/TWCardanoAddressTests.cpp | 2 +- .../{ => chains}/Cardano/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Cardano/TransactionTests.cpp | 0 tests/{ => chains}/Celo/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Cosmos/AddressTests.cpp | 0 tests/{ => chains}/Cosmos/Protobuf/.gitignore | 0 .../Cosmos/Protobuf/Article.proto | 0 tests/{ => chains}/Cosmos/ProtobufTests.cpp | 2 +- tests/{ => chains}/Cosmos/SignerTests.cpp | 2 +- tests/{ => chains}/Cosmos/StakingTests.cpp | 2 +- .../{ => chains}/Cosmos/TWAnyAddressTests.cpp | 2 +- .../{ => chains}/Cosmos/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Cosmos/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Cronos/TWAnyAddressTests.cpp | 2 +- tests/{ => chains}/Cronos/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/CryptoOrg/AddressTests.cpp | 0 tests/{ => chains}/CryptoOrg/SignerTests.cpp | 2 +- .../CryptoOrg/TWAnyAddressTests.cpp | 2 +- .../CryptoOrg/TWAnySignerTests.cpp | 2 +- .../CryptoOrg/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Dash/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Dash/TWDashTests.cpp | 2 +- tests/{ => chains}/Decred/AddressTests.cpp | 0 tests/{ => chains}/Decred/SignerTests.cpp | 0 .../{ => chains}/Decred/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Decred/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Decred/TWDecredTests.cpp | 2 +- .../{ => chains}/DigiByte/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/DigiByte/TWDigiByteTests.cpp | 2 +- .../{ => chains}/Dogecoin/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Dogecoin/TWDogeTests.cpp | 2 +- tests/{ => chains}/ECO/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/ECash/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/ECash/TWECashTests.cpp | 2 +- tests/{ => chains}/EOS/AddressTests.cpp | 0 tests/{ => chains}/EOS/AssetTests.cpp | 0 tests/{ => chains}/EOS/NameTests.cpp | 0 tests/{ => chains}/EOS/SignatureTests.cpp | 0 tests/{ => chains}/EOS/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/EOS/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/EOS/TransactionTests.cpp | 0 tests/{ => chains}/Elrond/AddressTests.cpp | 0 .../Elrond/SerializationTests.cpp | 0 tests/{ => chains}/Elrond/SignerTests.cpp | 0 .../{ => chains}/Elrond/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Elrond/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Elrond/TestAccounts.h | 0 .../Elrond/TransactionFactoryTests.cpp | 0 .../{ => chains}/Ethereum/AbiStructTests.cpp | 14 +++---- tests/{ => chains}/Ethereum/AbiTests.cpp | 2 +- tests/{ => chains}/Ethereum/AddressTests.cpp | 0 .../Ethereum/ContractCallTests.cpp | 22 +++++----- tests/{ => chains}/Ethereum/Data/1inch.json | 0 tests/{ => chains}/Ethereum/Data/custom.json | 0 .../Ethereum/Data/eip712_cryptofights.json | 0 .../Ethereum/Data/eip712_emptyArray.json | 0 .../Ethereum/Data/eip712_emptyString.json | 0 .../Ethereum/Data/eip712_rarible.json | 0 .../Ethereum/Data/eip712_snapshot_v4.json | 0 .../Ethereum/Data/eip712_walletconnect.json | 0 tests/{ => chains}/Ethereum/Data/ens.json | 0 tests/{ => chains}/Ethereum/Data/erc20.json | 0 tests/{ => chains}/Ethereum/Data/erc721.json | 0 .../Ethereum/Data/eth_feeHistory.json | 0 .../Ethereum/Data/eth_feeHistory2.json | 0 .../Ethereum/Data/eth_feeHistory3.json | 0 .../Ethereum/Data/eth_feeHistory4.json | 0 .../Ethereum/Data/getAmountsOut.json | 0 .../Ethereum/Data/kyber_proxy.json | 0 .../Ethereum/Data/seaport_712.json | 0 .../Ethereum/Data/tuple_nested.json | 0 .../Ethereum/Data/uniswap_router_v2.json | 0 .../Ethereum/Data/zilliqa_data_tx.json | 0 tests/{ => chains}/Ethereum/RLPTests.cpp | 0 tests/{ => chains}/Ethereum/SignerTests.cpp | 0 .../Ethereum/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Ethereum/TWCoinTypeTests.cpp | 2 +- .../Ethereum/TWEthereumAbiTests.cpp | 2 +- .../TWEthereumAbiValueDecoderTests.cpp | 2 +- .../TWEthereumAbiValueEncodeTests.cpp | 2 +- .../Ethereum/ValueDecoderTests.cpp | 0 .../Ethereum/ValueEncoderTests.cpp | 0 .../EthereumClassic/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Everscale/AddressTests.cpp | 0 .../Everscale/CellBuilderTest.cpp | 0 tests/{ => chains}/Everscale/CellTests.cpp | 0 tests/{ => chains}/Everscale/SignerTests.cpp | 0 .../Everscale/TWAnyAddressTests.cpp | 2 +- .../Everscale/TWAnySignerTests.cpp | 2 +- .../Everscale/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Evmos/SignerTests.cpp | 2 +- .../{ => chains}/Evmos/TWAnyAddressTests.cpp | 2 +- tests/{ => chains}/Evmos/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/FIO/AddressTests.cpp | 0 tests/{ => chains}/FIO/EncryptionTests.cpp | 0 tests/{ => chains}/FIO/SignerTests.cpp | 0 tests/{ => chains}/FIO/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/FIO/TWFIOAccountTests.cpp | 2 +- tests/{ => chains}/FIO/TWFIOTests.cpp | 2 +- .../FIO/TransactionBuilderTests.cpp | 0 tests/{ => chains}/Fantom/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Filecoin/AddressTests.cpp | 0 tests/{ => chains}/Filecoin/SignerTests.cpp | 0 .../Filecoin/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Filecoin/TWCoinTypeTests.cpp | 2 +- .../Filecoin/TransactionTests.cpp | 0 tests/{ => chains}/Firo/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Firo/TWZCoinAddressTests.cpp | 2 +- .../{ => chains}/GoChain/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Groestlcoin/AddressTests.cpp | 0 .../Groestlcoin/TWCoinTypeTests.cpp | 2 +- .../Groestlcoin/TWGroestlcoinSigningTests.cpp | 2 +- .../Groestlcoin/TWGroestlcoinTests.cpp | 2 +- tests/{ => chains}/Harmony/AddressTests.cpp | 0 tests/{ => chains}/Harmony/SignerTests.cpp | 0 tests/{ => chains}/Harmony/StakingTests.cpp | 0 .../Harmony/TWAnyAddressTests.cpp | 2 +- .../{ => chains}/Harmony/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Harmony/TWCoinTypeTests.cpp | 2 +- .../Harmony/TWHarmonyStakingTests.cpp | 2 +- tests/{Icon => chains/ICON}/AddressTests.cpp | 0 .../ICON}/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/ICON/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/IoTeX/AddressTests.cpp | 0 tests/{ => chains}/IoTeX/SignerTests.cpp | 0 tests/{ => chains}/IoTeX/StakingTests.cpp | 2 +- tests/{ => chains}/IoTeX/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/IoTeX/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Kava/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/KavaEvm/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Kin/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Klaytn/TWCoinTypeTests.cpp | 2 +- .../KuCoinCommunityChain/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Kusama/AddressTests.cpp | 0 tests/{ => chains}/Kusama/SignerTests.cpp | 0 .../{ => chains}/Kusama/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Kusama/TWCoinTypeTests.cpp | 2 +- .../Litecoin/LitecoinAddressTests.cpp | 0 .../{ => chains}/Litecoin/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Litecoin/TWLitecoinTests.cpp | 2 +- tests/{ => chains}/Meter/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Metis/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Monacoin/TWCoinTypeTests.cpp | 2 +- .../Monacoin/TWMonacoinAddressTests.cpp | 2 +- .../Monacoin/TWMonacoinTransactionTests.cpp | 2 +- .../{ => chains}/Moonbeam/TWCoinTypeTests.cpp | 2 +- .../Moonriver/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/NEAR/AccountTests.cpp | 0 tests/{ => chains}/NEAR/AddressTests.cpp | 0 .../{ => chains}/NEAR/SerializationTests.cpp | 0 tests/{ => chains}/NEAR/SignerTests.cpp | 0 tests/{ => chains}/NEAR/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/NEAR/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/NEAR/TWNEARAccountTests.cpp | 2 +- tests/{ => chains}/NEO/AddressTests.cpp | 0 tests/{ => chains}/NEO/CoinReferenceTests.cpp | 0 tests/{ => chains}/NEO/SignerTests.cpp | 0 tests/{ => chains}/NEO/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/NEO/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/NEO/TWNEOAddressTests.cpp | 2 +- .../NEO/TransactionAttributeTests.cpp | 0 .../NEO/TransactionOutputTests.cpp | 0 tests/{ => chains}/NEO/TransactionTests.cpp | 0 tests/{ => chains}/NEO/WitnessTests.cpp | 0 tests/{ => chains}/NULS/AddressTests.cpp | 0 tests/{ => chains}/NULS/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/NULS/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Nano/AddressTests.cpp | 0 tests/{ => chains}/Nano/SignerTests.cpp | 0 tests/{ => chains}/Nano/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Nano/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Nano/TWNanoAddressTests.cpp | 2 +- .../NativeEvmos/TWAnyAddressTests.cpp | 2 +- .../NativeEvmos/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Nebulas/AddressTests.cpp | 0 tests/{ => chains}/Nebulas/SignerTests.cpp | 0 .../{ => chains}/Nebulas/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Nebulas/TWCoinTypeTests.cpp | 2 +- .../Nebulas/TWNebulasAddressTests.cpp | 2 +- .../{ => chains}/Nebulas/TransactionTests.cpp | 0 tests/{ => chains}/Nervos/AddressTests.cpp | 0 tests/{ => chains}/Nervos/SignerTests.cpp | 2 +- .../{ => chains}/Nervos/TWAnyAddressTests.cpp | 2 +- .../{ => chains}/Nervos/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Nervos/TWCoinTypeTests.cpp | 2 +- .../Nervos/TWNervosAddressTests.cpp | 2 +- tests/{ => chains}/Nimiq/AddressTests.cpp | 0 tests/{ => chains}/Nimiq/SignerTests.cpp | 0 tests/{ => chains}/Nimiq/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Nimiq/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Nimiq/TransactionTests.cpp | 0 .../{ => chains}/OKXChain/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Oasis/AddressTests.cpp | 0 tests/{ => chains}/Oasis/SignerTests.cpp | 0 tests/{ => chains}/Oasis/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Oasis/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Ontology/AccountTests.cpp | 0 tests/{ => chains}/Ontology/AddressTests.cpp | 0 tests/{ => chains}/Ontology/Oep4Tests.cpp | 0 tests/{ => chains}/Ontology/OngTests.cpp | 0 tests/{ => chains}/Ontology/OntTests.cpp | 0 .../Ontology/ParamsBuilderTests.cpp | 0 .../Ontology/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Ontology/TWCoinTypeTests.cpp | 2 +- .../Ontology/TransactionTests.cpp | 0 .../{ => chains}/Optimism/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Osmosis/AddressTests.cpp | 0 tests/{ => chains}/Osmosis/SignerTests.cpp | 2 +- .../Osmosis/TWAnyAddressTests.cpp | 2 +- .../{ => chains}/Osmosis/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Osmosis/TWCoinTypeTests.cpp | 2 +- .../POANetwork/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Polkadot/AddressTests.cpp | 0 .../Polkadot/SS58AddressTests.cpp | 2 +- .../{ => chains}/Polkadot/ScaleCodecTests.cpp | 0 tests/{ => chains}/Polkadot/SignerTests.cpp | 0 .../{ => chains}/Polkadot/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Polygon/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Qtum/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Qtum/TWQtumAddressTests.cpp | 2 +- .../Ravencoin/TWCoinTypeTests.cpp | 2 +- .../Ravencoin/TWRavencoinTransactionTests.cpp | 2 +- .../{ => chains}/Ronin/TWAnyAddressTests.cpp | 2 +- tests/{ => chains}/Ronin/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Ronin/TWCoinTypeTests.cpp | 2 +- .../SmartBitcoinCash/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Solana/AddressTests.cpp | 0 tests/{ => chains}/Solana/ProgramTests.cpp | 0 tests/{ => chains}/Solana/SignerTests.cpp | 0 .../{ => chains}/Solana/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Solana/TWCoinTypeTests.cpp | 2 +- .../Solana/TWSolanaAddressTests.cpp | 2 +- .../{ => chains}/Solana/TransactionTests.cpp | 0 tests/{ => chains}/Stellar/AddressTests.cpp | 0 .../{ => chains}/Stellar/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Stellar/TWCoinTypeTests.cpp | 2 +- .../Stellar/TWStellarAddressTests.cpp | 2 +- .../{ => chains}/Stellar/TransactionTests.cpp | 2 +- tests/{ => chains}/THORChain/SignerTests.cpp | 2 +- tests/{ => chains}/THORChain/SwapTests.cpp | 2 +- .../THORChain/TWAnyAddressTests.cpp | 2 +- .../THORChain/TWAnySignerTests.cpp | 2 +- .../THORChain/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/THORChain/TWSwapTests.cpp | 2 +- tests/{ => chains}/Terra/SignerTests.cpp | 2 +- tests/{ => chains}/Terra/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/TerraV2/SignerTests.cpp | 2 +- .../{ => chains}/TerraV2/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Tezos/AddressTests.cpp | 0 tests/{ => chains}/Tezos/ForgingTests.cpp | 0 .../{ => chains}/Tezos/OperationListTests.cpp | 0 tests/{ => chains}/Tezos/PublicKeyTests.cpp | 0 tests/{ => chains}/Tezos/SignerTests.cpp | 0 tests/{ => chains}/Tezos/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Tezos/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Theta/SignerTests.cpp | 0 tests/{ => chains}/Theta/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Theta/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Theta/TransactionTests.cpp | 0 .../ThunderToken/TWCoinTypeTests.cpp | 2 +- .../TomoChain/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Tron/AddressTests.cpp | 0 .../{ => chains}/Tron/SerializationTests.cpp | 0 tests/{ => chains}/Tron/SignerTests.cpp | 0 tests/{ => chains}/Tron/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Tron/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/VeChain/SignerTests.cpp | 0 .../{ => chains}/VeChain/TWAnySignerTests.cpp | 2 +- .../{ => chains}/VeChain/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/Viacoin/TWCoinTypeTests.cpp | 2 +- .../Viacoin/TWViacoinAddressTests.cpp | 2 +- .../{ => chains}/Wanchain/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Waves/AddressTests.cpp | 0 tests/{ => chains}/Waves/LeaseTests.cpp | 0 tests/{ => chains}/Waves/SignerTests.cpp | 0 tests/{ => chains}/Waves/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/Waves/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/Waves/TransactionTests.cpp | 0 tests/{ => chains}/XRP/AddressTests.cpp | 0 tests/{ => chains}/XRP/TWAnySignerTests.cpp | 2 +- tests/{ => chains}/XRP/TWCoinTypeTests.cpp | 2 +- .../{ => chains}/XRP/TWRippleAddressTests.cpp | 2 +- tests/{ => chains}/XRP/TransactionTests.cpp | 0 tests/{ => chains}/Zcash/AddressTests.cpp | 0 tests/{ => chains}/Zcash/TWCoinTypeTests.cpp | 2 +- .../Zcash/TWZcashAddressTests.cpp | 2 +- .../Zcash/TWZcashTransactionTests.cpp | 2 +- .../{ => chains}/Zelcash/TWCoinTypeTests.cpp | 2 +- .../Zelcash/TWZelcashAddressTests.cpp | 2 +- .../Zelcash/TWZelcashTransactionTests.cpp | 2 +- tests/{ => chains}/Zilliqa/AddressTests.cpp | 0 tests/{ => chains}/Zilliqa/SignatureTests.cpp | 2 +- tests/{ => chains}/Zilliqa/SignerTests.cpp | 0 .../{ => chains}/Zilliqa/TWAnySignerTests.cpp | 2 +- .../{ => chains}/Zilliqa/TWCoinTypeTests.cpp | 2 +- .../Zilliqa/TWZilliqaAddressTests.cpp | 2 +- .../{ => chains}/ZkSyncV2/TWCoinTypeTests.cpp | 2 +- tests/{ => chains}/xDai/TWCoinTypeTests.cpp | 2 +- tests/{ => common}/AnyAddressTests.cpp | 0 tests/{ => common}/BCSTests.cpp | 0 tests/{ => common}/Base64Tests.cpp | 0 tests/{ => common}/BaseEncoding.cpp | 0 tests/{ => common}/Bech32AddressTests.cpp | 0 tests/{ => common}/Bech32Tests.cpp | 0 tests/{ => common}/BinaryCodingTests.cpp | 0 tests/{ => common}/CborTests.cpp | 0 .../CoinAddressDerivationTests.cpp | 0 .../CoinAddressValidationTests.cpp | 0 tests/{ => common}/DataTests.cpp | 0 tests/{ => common}/EncryptTests.cpp | 0 .../HDWallet/HDWalletInternalTests.cpp | 2 +- tests/{ => common}/HDWallet/HDWalletTests.cpp | 4 +- .../{ => common}/HDWallet/bip39_vectors.json | 0 tests/{ => common}/HashTests.cpp | 0 tests/{ => common}/HexCodingTests.cpp | 0 .../Keystore/Data/empty-accounts.json | 0 .../Data/ethereum-wallet-address-no-0x.json | 0 tests/{ => common}/Keystore/Data/key.json | 0 .../Keystore/Data/key_bitcoin.json | 0 .../Keystore/Data/legacy-mnemonic.json | 0 .../Keystore/Data/legacy-private-key.json | 0 .../{ => common}/Keystore/Data/livepeer.json | 0 .../Keystore/Data/missing-address.json | 0 .../Keystore/Data/myetherwallet.uu | 0 tests/{ => common}/Keystore/Data/pbkdf2.json | 0 tests/{ => common}/Keystore/Data/wallet.json | 0 tests/{ => common}/Keystore/Data/watch.json | 0 tests/{ => common}/Keystore/Data/web3j.json | 0 .../Keystore/DerivationPathTests.cpp | 0 .../{ => common}/Keystore/StoredKeyTests.cpp | 40 ++++++++++--------- tests/{ => common}/MnemonicTests.cpp | 0 tests/{ => common}/NumericLiteralTests.cpp | 0 tests/{ => common}/PrivateKeyTests.cpp | 0 tests/{ => common}/PublicKeyTests.cpp | 2 +- .../TestUtilities.cpp} | 2 +- .../TestUtilities.h} | 0 .../{ => common}/TransactionCompilerTests.cpp | 2 +- tests/{ => common}/Uint256Tests.cpp | 0 tests/{ => common}/WalletConsoleTests.cpp | 0 tests/{ => common}/algorithm/erase_tests.cpp | 0 .../algorithm/sort_copy_tests.cpp | 0 .../{ => common}/algorithm/to_array_tests.cpp | 0 tests/{ => common}/memory/memzero_tests.cpp | 0 .../operators/equality_comparable_tests.cpp | 0 tests/interface/TWAESTests.cpp | 2 +- tests/interface/TWAccountTests.cpp | 2 +- tests/interface/TWAnyAddressTests.cpp | 2 +- tests/interface/TWBase32Tests.cpp | 2 +- tests/interface/TWBase58Tests.cpp | 2 +- tests/interface/TWBase64Tests.cpp | 2 +- tests/interface/TWCoinTypeTests.cpp | 2 +- tests/interface/TWDataTests.cpp | 2 +- tests/interface/TWDataVectorTests.cpp | 2 +- tests/interface/TWDerivationPathTests.cpp | 2 +- tests/interface/TWHDWalletTests.cpp | 2 +- tests/interface/TWHRPTests.cpp | 2 +- tests/interface/TWHashTests.cpp | 2 +- tests/interface/TWMnemonicTests.cpp | 2 +- tests/interface/TWPBKDF2Tests.cpp | 2 +- tests/interface/TWPrivateKeyTests.cpp | 2 +- tests/interface/TWPublicKeyTests.cpp | 2 +- tests/interface/TWStoredKeyTests.cpp | 4 +- tests/interface/TWStringTests.cpp | 2 +- .../interface/TWTransactionCompilerTests.cpp | 2 +- tools/generate-files | 2 +- 433 files changed, 289 insertions(+), 285 deletions(-) rename tests/{ => chains}/Aeternity/AddressTests.cpp (100%) rename tests/{ => chains}/Aeternity/SignerTests.cpp (100%) rename tests/{ => chains}/Aeternity/TWAeternityAddressTests.cpp (96%) rename tests/{ => chains}/Aeternity/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Aeternity/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Aeternity/TransactionTests.cpp (98%) rename tests/{ => chains}/Aion/AddressTests.cpp (100%) rename tests/{ => chains}/Aion/RLPTests.cpp (100%) rename tests/{ => chains}/Aion/SignerTests.cpp (100%) rename tests/{ => chains}/Aion/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Aion/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Aion/TransactionTests.cpp (100%) rename tests/{ => chains}/Algorand/AddressTests.cpp (100%) rename tests/{ => chains}/Algorand/SignerTests.cpp (100%) rename tests/{ => chains}/Algorand/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Algorand/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Aptos/AddressTests.cpp (100%) rename tests/{ => chains}/Aptos/MoveTypesTests.cpp (100%) rename tests/{ => chains}/Aptos/SignerTests.cpp (99%) rename tests/{ => chains}/Aptos/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Aptos/TWAptosAddressTests.cpp (96%) rename tests/{ => chains}/Aptos/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Aptos/TransactionPayloadTests.cpp (100%) rename tests/{ => chains}/Arbitrum/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Aurora/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Avalanche/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/BandChain/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Binance/SignerTests.cpp (100%) rename tests/{ => chains}/Binance/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/Binance/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/BinanceSmartChain/SignerTests.cpp (98%) rename tests/{ => chains}/BinanceSmartChain/TWAnyAddressTests.cpp (96%) rename tests/{ => chains}/BinanceSmartChain/TWCoinTypeTests.cpp (98%) rename tests/{ => chains}/Bitcoin/BitcoinAddressTests.cpp (100%) rename tests/{ => chains}/Bitcoin/BitcoinScriptTests.cpp (99%) rename tests/{ => chains}/Bitcoin/FeeCalculatorTests.cpp (100%) rename tests/{ => chains}/Bitcoin/InputSelectorTests.cpp (100%) rename tests/{ => chains}/Bitcoin/MessageSignerTests.cpp (99%) rename tests/{ => chains}/Bitcoin/SegwitAddressTests.cpp (100%) rename tests/{ => chains}/Bitcoin/TWBitcoinAddressTests.cpp (98%) rename tests/{ => chains}/Bitcoin/TWBitcoinScriptTests.cpp (99%) rename tests/{ => chains}/Bitcoin/TWBitcoinSigningTests.cpp (100%) rename tests/{ => chains}/Bitcoin/TWBitcoinTransactionTests.cpp (100%) rename tests/{ => chains}/Bitcoin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Bitcoin/TWSegwitAddressTests.cpp (98%) rename tests/{ => chains}/Bitcoin/TransactionPlanTests.cpp (100%) rename tests/{ => chains}/Bitcoin/TxComparisonHelper.cpp (100%) rename tests/{ => chains}/Bitcoin/TxComparisonHelper.h (100%) rename tests/{ => chains}/BitcoinCash/TWBitcoinCashTests.cpp (99%) rename tests/{ => chains}/BitcoinCash/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/BitcoinGold/TWAddressTests.cpp (96%) rename tests/{ => chains}/BitcoinGold/TWBitcoinGoldTests.cpp (99%) rename tests/{ => chains}/BitcoinGold/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/BitcoinGold/TWSegwitAddressTests.cpp (98%) rename tests/{ => chains}/BitcoinGold/TWSignerTests.cpp (98%) rename tests/{ => chains}/Bluzelle/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Boba/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Callisto/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Cardano/AddressTests.cpp (100%) rename tests/{ => chains}/Cardano/SigningTests.cpp (99%) rename tests/{ => chains}/Cardano/StakingTests.cpp (99%) rename tests/{ => chains}/Cardano/TWCardanoAddressTests.cpp (99%) rename tests/{ => chains}/Cardano/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Cardano/TransactionTests.cpp (100%) rename tests/{ => chains}/Celo/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Cosmos/AddressTests.cpp (100%) rename tests/{ => chains}/Cosmos/Protobuf/.gitignore (100%) rename tests/{ => chains}/Cosmos/Protobuf/Article.proto (100%) rename tests/{ => chains}/Cosmos/ProtobufTests.cpp (98%) rename tests/{ => chains}/Cosmos/SignerTests.cpp (99%) rename tests/{ => chains}/Cosmos/StakingTests.cpp (99%) rename tests/{ => chains}/Cosmos/TWAnyAddressTests.cpp (95%) rename tests/{ => chains}/Cosmos/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Cosmos/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Cronos/TWAnyAddressTests.cpp (94%) rename tests/{ => chains}/Cronos/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/CryptoOrg/AddressTests.cpp (100%) rename tests/{ => chains}/CryptoOrg/SignerTests.cpp (99%) rename tests/{ => chains}/CryptoOrg/TWAnyAddressTests.cpp (96%) rename tests/{ => chains}/CryptoOrg/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/CryptoOrg/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Dash/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Dash/TWDashTests.cpp (95%) rename tests/{ => chains}/Decred/AddressTests.cpp (100%) rename tests/{ => chains}/Decred/SignerTests.cpp (100%) rename tests/{ => chains}/Decred/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/Decred/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Decred/TWDecredTests.cpp (98%) rename tests/{ => chains}/DigiByte/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/DigiByte/TWDigiByteTests.cpp (99%) rename tests/{ => chains}/Dogecoin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Dogecoin/TWDogeTests.cpp (95%) rename tests/{ => chains}/ECO/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/ECash/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/ECash/TWECashTests.cpp (99%) rename tests/{ => chains}/EOS/AddressTests.cpp (100%) rename tests/{ => chains}/EOS/AssetTests.cpp (100%) rename tests/{ => chains}/EOS/NameTests.cpp (100%) rename tests/{ => chains}/EOS/SignatureTests.cpp (100%) rename tests/{ => chains}/EOS/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/EOS/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/EOS/TransactionTests.cpp (100%) rename tests/{ => chains}/Elrond/AddressTests.cpp (100%) rename tests/{ => chains}/Elrond/SerializationTests.cpp (100%) rename tests/{ => chains}/Elrond/SignerTests.cpp (100%) rename tests/{ => chains}/Elrond/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Elrond/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Elrond/TestAccounts.h (100%) rename tests/{ => chains}/Elrond/TransactionFactoryTests.cpp (100%) rename tests/{ => chains}/Ethereum/AbiStructTests.cpp (99%) rename tests/{ => chains}/Ethereum/AbiTests.cpp (99%) rename tests/{ => chains}/Ethereum/AddressTests.cpp (100%) rename tests/{ => chains}/Ethereum/ContractCallTests.cpp (95%) rename tests/{ => chains}/Ethereum/Data/1inch.json (100%) rename tests/{ => chains}/Ethereum/Data/custom.json (100%) rename tests/{ => chains}/Ethereum/Data/eip712_cryptofights.json (100%) rename tests/{ => chains}/Ethereum/Data/eip712_emptyArray.json (100%) rename tests/{ => chains}/Ethereum/Data/eip712_emptyString.json (100%) rename tests/{ => chains}/Ethereum/Data/eip712_rarible.json (100%) rename tests/{ => chains}/Ethereum/Data/eip712_snapshot_v4.json (100%) rename tests/{ => chains}/Ethereum/Data/eip712_walletconnect.json (100%) rename tests/{ => chains}/Ethereum/Data/ens.json (100%) rename tests/{ => chains}/Ethereum/Data/erc20.json (100%) rename tests/{ => chains}/Ethereum/Data/erc721.json (100%) rename tests/{ => chains}/Ethereum/Data/eth_feeHistory.json (100%) rename tests/{ => chains}/Ethereum/Data/eth_feeHistory2.json (100%) rename tests/{ => chains}/Ethereum/Data/eth_feeHistory3.json (100%) rename tests/{ => chains}/Ethereum/Data/eth_feeHistory4.json (100%) rename tests/{ => chains}/Ethereum/Data/getAmountsOut.json (100%) rename tests/{ => chains}/Ethereum/Data/kyber_proxy.json (100%) rename tests/{ => chains}/Ethereum/Data/seaport_712.json (100%) rename tests/{ => chains}/Ethereum/Data/tuple_nested.json (100%) rename tests/{ => chains}/Ethereum/Data/uniswap_router_v2.json (100%) rename tests/{ => chains}/Ethereum/Data/zilliqa_data_tx.json (100%) rename tests/{ => chains}/Ethereum/RLPTests.cpp (100%) rename tests/{ => chains}/Ethereum/SignerTests.cpp (100%) rename tests/{ => chains}/Ethereum/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/Ethereum/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Ethereum/TWEthereumAbiTests.cpp (99%) rename tests/{ => chains}/Ethereum/TWEthereumAbiValueDecoderTests.cpp (99%) rename tests/{ => chains}/Ethereum/TWEthereumAbiValueEncodeTests.cpp (98%) rename tests/{ => chains}/Ethereum/ValueDecoderTests.cpp (100%) rename tests/{ => chains}/Ethereum/ValueEncoderTests.cpp (100%) rename tests/{ => chains}/EthereumClassic/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Everscale/AddressTests.cpp (100%) rename tests/{ => chains}/Everscale/CellBuilderTest.cpp (100%) rename tests/{ => chains}/Everscale/CellTests.cpp (100%) rename tests/{ => chains}/Everscale/SignerTests.cpp (100%) rename tests/{ => chains}/Everscale/TWAnyAddressTests.cpp (96%) rename tests/{ => chains}/Everscale/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Everscale/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Evmos/SignerTests.cpp (99%) rename tests/{ => chains}/Evmos/TWAnyAddressTests.cpp (97%) rename tests/{ => chains}/Evmos/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/FIO/AddressTests.cpp (100%) rename tests/{ => chains}/FIO/EncryptionTests.cpp (100%) rename tests/{ => chains}/FIO/SignerTests.cpp (100%) rename tests/{ => chains}/FIO/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/FIO/TWFIOAccountTests.cpp (97%) rename tests/{ => chains}/FIO/TWFIOTests.cpp (99%) rename tests/{ => chains}/FIO/TransactionBuilderTests.cpp (100%) rename tests/{ => chains}/Fantom/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Filecoin/AddressTests.cpp (100%) rename tests/{ => chains}/Filecoin/SignerTests.cpp (100%) rename tests/{ => chains}/Filecoin/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Filecoin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Filecoin/TransactionTests.cpp (100%) rename tests/{ => chains}/Firo/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Firo/TWZCoinAddressTests.cpp (98%) rename tests/{ => chains}/GoChain/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Groestlcoin/AddressTests.cpp (100%) rename tests/{ => chains}/Groestlcoin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Groestlcoin/TWGroestlcoinSigningTests.cpp (99%) rename tests/{ => chains}/Groestlcoin/TWGroestlcoinTests.cpp (99%) rename tests/{ => chains}/Harmony/AddressTests.cpp (100%) rename tests/{ => chains}/Harmony/SignerTests.cpp (100%) rename tests/{ => chains}/Harmony/StakingTests.cpp (100%) rename tests/{ => chains}/Harmony/TWAnyAddressTests.cpp (95%) rename tests/{ => chains}/Harmony/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Harmony/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Harmony/TWHarmonyStakingTests.cpp (99%) rename tests/{Icon => chains/ICON}/AddressTests.cpp (100%) rename tests/{Icon => chains/ICON}/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/ICON/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/IoTeX/AddressTests.cpp (100%) rename tests/{ => chains}/IoTeX/SignerTests.cpp (100%) rename tests/{ => chains}/IoTeX/StakingTests.cpp (99%) rename tests/{ => chains}/IoTeX/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/IoTeX/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Kava/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/KavaEvm/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Kin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Klaytn/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/KuCoinCommunityChain/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Kusama/AddressTests.cpp (100%) rename tests/{ => chains}/Kusama/SignerTests.cpp (100%) rename tests/{ => chains}/Kusama/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Kusama/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Litecoin/LitecoinAddressTests.cpp (100%) rename tests/{ => chains}/Litecoin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Litecoin/TWLitecoinTests.cpp (99%) rename tests/{ => chains}/Meter/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Metis/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Monacoin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Monacoin/TWMonacoinAddressTests.cpp (99%) rename tests/{ => chains}/Monacoin/TWMonacoinTransactionTests.cpp (99%) rename tests/{ => chains}/Moonbeam/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Moonriver/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/NEAR/AccountTests.cpp (100%) rename tests/{ => chains}/NEAR/AddressTests.cpp (100%) rename tests/{ => chains}/NEAR/SerializationTests.cpp (100%) rename tests/{ => chains}/NEAR/SignerTests.cpp (100%) rename tests/{ => chains}/NEAR/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/NEAR/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/NEAR/TWNEARAccountTests.cpp (96%) rename tests/{ => chains}/NEO/AddressTests.cpp (100%) rename tests/{ => chains}/NEO/CoinReferenceTests.cpp (100%) rename tests/{ => chains}/NEO/SignerTests.cpp (100%) rename tests/{ => chains}/NEO/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/NEO/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/NEO/TWNEOAddressTests.cpp (96%) rename tests/{ => chains}/NEO/TransactionAttributeTests.cpp (100%) rename tests/{ => chains}/NEO/TransactionOutputTests.cpp (100%) rename tests/{ => chains}/NEO/TransactionTests.cpp (100%) rename tests/{ => chains}/NEO/WitnessTests.cpp (100%) rename tests/{ => chains}/NULS/AddressTests.cpp (100%) rename tests/{ => chains}/NULS/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/NULS/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Nano/AddressTests.cpp (100%) rename tests/{ => chains}/Nano/SignerTests.cpp (100%) rename tests/{ => chains}/Nano/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Nano/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Nano/TWNanoAddressTests.cpp (97%) rename tests/{ => chains}/NativeEvmos/TWAnyAddressTests.cpp (97%) rename tests/{ => chains}/NativeEvmos/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Nebulas/AddressTests.cpp (100%) rename tests/{ => chains}/Nebulas/SignerTests.cpp (100%) rename tests/{ => chains}/Nebulas/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Nebulas/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Nebulas/TWNebulasAddressTests.cpp (98%) rename tests/{ => chains}/Nebulas/TransactionTests.cpp (100%) rename tests/{ => chains}/Nervos/AddressTests.cpp (100%) rename tests/{ => chains}/Nervos/SignerTests.cpp (99%) rename tests/{ => chains}/Nervos/TWAnyAddressTests.cpp (98%) rename tests/{ => chains}/Nervos/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/Nervos/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Nervos/TWNervosAddressTests.cpp (96%) rename tests/{ => chains}/Nimiq/AddressTests.cpp (100%) rename tests/{ => chains}/Nimiq/SignerTests.cpp (100%) rename tests/{ => chains}/Nimiq/TWAnySignerTests.cpp (96%) rename tests/{ => chains}/Nimiq/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Nimiq/TransactionTests.cpp (100%) rename tests/{ => chains}/OKXChain/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Oasis/AddressTests.cpp (100%) rename tests/{ => chains}/Oasis/SignerTests.cpp (100%) rename tests/{ => chains}/Oasis/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Oasis/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Ontology/AccountTests.cpp (100%) rename tests/{ => chains}/Ontology/AddressTests.cpp (100%) rename tests/{ => chains}/Ontology/Oep4Tests.cpp (100%) rename tests/{ => chains}/Ontology/OngTests.cpp (100%) rename tests/{ => chains}/Ontology/OntTests.cpp (100%) rename tests/{ => chains}/Ontology/ParamsBuilderTests.cpp (100%) rename tests/{ => chains}/Ontology/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/Ontology/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Ontology/TransactionTests.cpp (100%) rename tests/{ => chains}/Optimism/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Osmosis/AddressTests.cpp (100%) rename tests/{ => chains}/Osmosis/SignerTests.cpp (98%) rename tests/{ => chains}/Osmosis/TWAnyAddressTests.cpp (96%) rename tests/{ => chains}/Osmosis/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Osmosis/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/POANetwork/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Polkadot/AddressTests.cpp (100%) rename tests/{ => chains}/Polkadot/SS58AddressTests.cpp (99%) rename tests/{ => chains}/Polkadot/ScaleCodecTests.cpp (100%) rename tests/{ => chains}/Polkadot/SignerTests.cpp (100%) rename tests/{ => chains}/Polkadot/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Polygon/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Qtum/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Qtum/TWQtumAddressTests.cpp (99%) rename tests/{ => chains}/Ravencoin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Ravencoin/TWRavencoinTransactionTests.cpp (99%) rename tests/{ => chains}/Ronin/TWAnyAddressTests.cpp (98%) rename tests/{ => chains}/Ronin/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Ronin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/SmartBitcoinCash/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Solana/AddressTests.cpp (100%) rename tests/{ => chains}/Solana/ProgramTests.cpp (100%) rename tests/{ => chains}/Solana/SignerTests.cpp (100%) rename tests/{ => chains}/Solana/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/Solana/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Solana/TWSolanaAddressTests.cpp (97%) rename tests/{ => chains}/Solana/TransactionTests.cpp (100%) rename tests/{ => chains}/Stellar/AddressTests.cpp (100%) rename tests/{ => chains}/Stellar/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/Stellar/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Stellar/TWStellarAddressTests.cpp (96%) rename tests/{ => chains}/Stellar/TransactionTests.cpp (99%) rename tests/{ => chains}/THORChain/SignerTests.cpp (99%) rename tests/{ => chains}/THORChain/SwapTests.cpp (99%) rename tests/{ => chains}/THORChain/TWAnyAddressTests.cpp (96%) rename tests/{ => chains}/THORChain/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/THORChain/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/THORChain/TWSwapTests.cpp (99%) rename tests/{ => chains}/Terra/SignerTests.cpp (99%) rename tests/{ => chains}/Terra/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/TerraV2/SignerTests.cpp (99%) rename tests/{ => chains}/TerraV2/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Tezos/AddressTests.cpp (100%) rename tests/{ => chains}/Tezos/ForgingTests.cpp (100%) rename tests/{ => chains}/Tezos/OperationListTests.cpp (100%) rename tests/{ => chains}/Tezos/PublicKeyTests.cpp (100%) rename tests/{ => chains}/Tezos/SignerTests.cpp (100%) rename tests/{ => chains}/Tezos/TWAnySignerTests.cpp (99%) rename tests/{ => chains}/Tezos/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Theta/SignerTests.cpp (100%) rename tests/{ => chains}/Theta/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Theta/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Theta/TransactionTests.cpp (100%) rename tests/{ => chains}/ThunderToken/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/TomoChain/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Tron/AddressTests.cpp (100%) rename tests/{ => chains}/Tron/SerializationTests.cpp (100%) rename tests/{ => chains}/Tron/SignerTests.cpp (100%) rename tests/{ => chains}/Tron/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Tron/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/VeChain/SignerTests.cpp (100%) rename tests/{ => chains}/VeChain/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/VeChain/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Viacoin/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Viacoin/TWViacoinAddressTests.cpp (99%) rename tests/{ => chains}/Wanchain/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Waves/AddressTests.cpp (100%) rename tests/{ => chains}/Waves/LeaseTests.cpp (100%) rename tests/{ => chains}/Waves/SignerTests.cpp (100%) rename tests/{ => chains}/Waves/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/Waves/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Waves/TransactionTests.cpp (100%) rename tests/{ => chains}/XRP/AddressTests.cpp (100%) rename tests/{ => chains}/XRP/TWAnySignerTests.cpp (97%) rename tests/{ => chains}/XRP/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/XRP/TWRippleAddressTests.cpp (97%) rename tests/{ => chains}/XRP/TransactionTests.cpp (100%) rename tests/{ => chains}/Zcash/AddressTests.cpp (100%) rename tests/{ => chains}/Zcash/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Zcash/TWZcashAddressTests.cpp (99%) rename tests/{ => chains}/Zcash/TWZcashTransactionTests.cpp (99%) rename tests/{ => chains}/Zelcash/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Zelcash/TWZelcashAddressTests.cpp (99%) rename tests/{ => chains}/Zelcash/TWZelcashTransactionTests.cpp (99%) rename tests/{ => chains}/Zilliqa/AddressTests.cpp (100%) rename tests/{ => chains}/Zilliqa/SignatureTests.cpp (97%) rename tests/{ => chains}/Zilliqa/SignerTests.cpp (100%) rename tests/{ => chains}/Zilliqa/TWAnySignerTests.cpp (98%) rename tests/{ => chains}/Zilliqa/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/Zilliqa/TWZilliqaAddressTests.cpp (97%) rename tests/{ => chains}/ZkSyncV2/TWCoinTypeTests.cpp (97%) rename tests/{ => chains}/xDai/TWCoinTypeTests.cpp (97%) rename tests/{ => common}/AnyAddressTests.cpp (100%) rename tests/{ => common}/BCSTests.cpp (100%) rename tests/{ => common}/Base64Tests.cpp (100%) rename tests/{ => common}/BaseEncoding.cpp (100%) rename tests/{ => common}/Bech32AddressTests.cpp (100%) rename tests/{ => common}/Bech32Tests.cpp (100%) rename tests/{ => common}/BinaryCodingTests.cpp (100%) rename tests/{ => common}/CborTests.cpp (100%) rename tests/{ => common}/CoinAddressDerivationTests.cpp (100%) rename tests/{ => common}/CoinAddressValidationTests.cpp (100%) rename tests/{ => common}/DataTests.cpp (100%) rename tests/{ => common}/EncryptTests.cpp (100%) rename tests/{ => common}/HDWallet/HDWalletInternalTests.cpp (99%) rename tests/{ => common}/HDWallet/HDWalletTests.cpp (99%) rename tests/{ => common}/HDWallet/bip39_vectors.json (100%) rename tests/{ => common}/HashTests.cpp (100%) rename tests/{ => common}/HexCodingTests.cpp (100%) rename tests/{ => common}/Keystore/Data/empty-accounts.json (100%) rename tests/{ => common}/Keystore/Data/ethereum-wallet-address-no-0x.json (100%) rename tests/{ => common}/Keystore/Data/key.json (100%) rename tests/{ => common}/Keystore/Data/key_bitcoin.json (100%) rename tests/{ => common}/Keystore/Data/legacy-mnemonic.json (100%) rename tests/{ => common}/Keystore/Data/legacy-private-key.json (100%) rename tests/{ => common}/Keystore/Data/livepeer.json (100%) rename tests/{ => common}/Keystore/Data/missing-address.json (100%) rename tests/{ => common}/Keystore/Data/myetherwallet.uu (100%) rename tests/{ => common}/Keystore/Data/pbkdf2.json (100%) rename tests/{ => common}/Keystore/Data/wallet.json (100%) rename tests/{ => common}/Keystore/Data/watch.json (100%) rename tests/{ => common}/Keystore/Data/web3j.json (100%) rename tests/{ => common}/Keystore/DerivationPathTests.cpp (100%) rename tests/{ => common}/Keystore/StoredKeyTests.cpp (95%) rename tests/{ => common}/MnemonicTests.cpp (100%) rename tests/{ => common}/NumericLiteralTests.cpp (100%) rename tests/{ => common}/PrivateKeyTests.cpp (100%) rename tests/{ => common}/PublicKeyTests.cpp (99%) rename tests/{interface/TWTestUtilities.cpp => common/TestUtilities.cpp} (96%) rename tests/{interface/TWTestUtilities.h => common/TestUtilities.h} (100%) rename tests/{ => common}/TransactionCompilerTests.cpp (99%) rename tests/{ => common}/Uint256Tests.cpp (100%) rename tests/{ => common}/WalletConsoleTests.cpp (100%) rename tests/{ => common}/algorithm/erase_tests.cpp (100%) rename tests/{ => common}/algorithm/sort_copy_tests.cpp (100%) rename tests/{ => common}/algorithm/to_array_tests.cpp (100%) rename tests/{ => common}/memory/memzero_tests.cpp (100%) rename tests/{ => common}/operators/equality_comparable_tests.cpp (100%) diff --git a/codegen/bin/newcoin b/codegen/bin/newcoin index af985bd292b..8fdacb210fe 100755 --- a/codegen/bin/newcoin +++ b/codegen/bin/newcoin @@ -134,10 +134,10 @@ generate_file("newcoin/Proto.erb", "src/proto", "#{name}.proto", coin) generate_file("newcoin/Signer.h.erb", "src/#{name}", "Signer.h", coin) generate_file("newcoin/Signer.cpp.erb", "src/#{name}", "Signer.cpp", coin) -generate_file("newcoin/AddressTests.cpp.erb", "tests/#{name}", "AddressTests.cpp", coin) -generate_file("newcoin/SignerTests.cpp.erb", "tests/#{name}", "SignerTests.cpp", coin) -generate_file("newcoin/TWAddressTests.cpp.erb", "tests/#{name}", "TWAnyAddressTests.cpp", coin) -generate_file("newcoin/TWSignerTests.cpp.erb", "tests/#{name}", "TWAnySignerTests.cpp", coin) +generate_file("newcoin/AddressTests.cpp.erb", "tests/chains/#{name}", "AddressTests.cpp", coin) +generate_file("newcoin/SignerTests.cpp.erb", "tests/chains/#{name}", "SignerTests.cpp", coin) +generate_file("newcoin/TWAddressTests.cpp.erb", "tests/chains/#{name}", "TWAnyAddressTests.cpp", coin) +generate_file("newcoin/TWSignerTests.cpp.erb", "tests/chains/#{name}", "TWAnySignerTests.cpp", coin) generate_file("newcoin/AddressTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Address.kt", coin) generate_file("newcoin/SignerTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Signer.kt", coin) generate_file("newcoin/Tests.swift.erb", "swift/Tests/Blockchains", "#{name}Tests.swift", coin) diff --git a/codegen/lib/coin_test_gen.rb b/codegen/lib/coin_test_gen.rb index e4d7d845fce..0b831bfb277 100755 --- a/codegen/lib/coin_test_gen.rb +++ b/codegen/lib/coin_test_gen.rb @@ -63,7 +63,7 @@ def generate_coin_test_file(coin, templateFile, overwriteExisting = true) template = ERB.new(File.read(path), nil, '-') result = template.result(binding) - folder = 'tests/' + folder = 'tests/chains/' if coin.key?('testFolderName') folder += format_name(coin['testFolderName']) else diff --git a/codegen/lib/templates/TWCoinTypeTests.cpp.erb b/codegen/lib/templates/TWCoinTypeTests.cpp.erb index 4c8cd77e4fc..ab2a8fbf9c3 100644 --- a/codegen/lib/templates/TWCoinTypeTests.cpp.erb +++ b/codegen/lib/templates/TWCoinTypeTests.cpp.erb @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb b/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb index 0a4ea03764d..b290debae7a 100644 --- a/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb +++ b/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb @@ -7,7 +7,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb b/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb index 4e72ef5fa5f..59b6aa225c6 100644 --- a/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb +++ b/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb @@ -7,7 +7,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/swift/Tests/Blockchains/Data b/swift/Tests/Blockchains/Data index f50c5d874d3..154e08de1f2 120000 --- a/swift/Tests/Blockchains/Data +++ b/swift/Tests/Blockchains/Data @@ -1 +1 @@ -../../../tests/Ethereum/Data \ No newline at end of file +../../../tests/chains/Ethereum/Data \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 746cff782a1..b48482ab71b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,6 +25,7 @@ file(GLOB_RECURSE test_sources *.cpp **/*.cpp **/*.cc) add_executable(tests ${test_sources}) target_link_libraries(tests gtest_main TrezorCrypto TrustWalletCore walletconsolelib protobuf Boost::boost) target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/src) +target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/tests/common) target_compile_options(tests PRIVATE "-Wall") if (NOT ANDROID AND TW_UNITY_BUILD) set_target_properties(tests PROPERTIES UNITY_BUILD ON) diff --git a/tests/Aeternity/AddressTests.cpp b/tests/chains/Aeternity/AddressTests.cpp similarity index 100% rename from tests/Aeternity/AddressTests.cpp rename to tests/chains/Aeternity/AddressTests.cpp diff --git a/tests/Aeternity/SignerTests.cpp b/tests/chains/Aeternity/SignerTests.cpp similarity index 100% rename from tests/Aeternity/SignerTests.cpp rename to tests/chains/Aeternity/SignerTests.cpp diff --git a/tests/Aeternity/TWAeternityAddressTests.cpp b/tests/chains/Aeternity/TWAeternityAddressTests.cpp similarity index 96% rename from tests/Aeternity/TWAeternityAddressTests.cpp rename to tests/chains/Aeternity/TWAeternityAddressTests.cpp index 51376f5cf7a..b801091634c 100644 --- a/tests/Aeternity/TWAeternityAddressTests.cpp +++ b/tests/chains/Aeternity/TWAeternityAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aeternity/TWAnySignerTests.cpp b/tests/chains/Aeternity/TWAnySignerTests.cpp similarity index 97% rename from tests/Aeternity/TWAnySignerTests.cpp rename to tests/chains/Aeternity/TWAnySignerTests.cpp index f966db42fbe..9a1f716447f 100644 --- a/tests/Aeternity/TWAnySignerTests.cpp +++ b/tests/chains/Aeternity/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include "proto/Aeternity.pb.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Aeternity::tests { diff --git a/tests/Aeternity/TWCoinTypeTests.cpp b/tests/chains/Aeternity/TWCoinTypeTests.cpp similarity index 97% rename from tests/Aeternity/TWCoinTypeTests.cpp rename to tests/chains/Aeternity/TWCoinTypeTests.cpp index 7db96db7589..7e9f16603ca 100644 --- a/tests/Aeternity/TWCoinTypeTests.cpp +++ b/tests/chains/Aeternity/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aeternity/TransactionTests.cpp b/tests/chains/Aeternity/TransactionTests.cpp similarity index 98% rename from tests/Aeternity/TransactionTests.cpp rename to tests/chains/Aeternity/TransactionTests.cpp index ea0ac8aedd3..fa50abb449f 100644 --- a/tests/Aeternity/TransactionTests.cpp +++ b/tests/chains/Aeternity/TransactionTests.cpp @@ -6,7 +6,7 @@ #include "HexCoding.h" #include "PrivateKey.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aion/AddressTests.cpp b/tests/chains/Aion/AddressTests.cpp similarity index 100% rename from tests/Aion/AddressTests.cpp rename to tests/chains/Aion/AddressTests.cpp diff --git a/tests/Aion/RLPTests.cpp b/tests/chains/Aion/RLPTests.cpp similarity index 100% rename from tests/Aion/RLPTests.cpp rename to tests/chains/Aion/RLPTests.cpp diff --git a/tests/Aion/SignerTests.cpp b/tests/chains/Aion/SignerTests.cpp similarity index 100% rename from tests/Aion/SignerTests.cpp rename to tests/chains/Aion/SignerTests.cpp diff --git a/tests/Aion/TWAnySignerTests.cpp b/tests/chains/Aion/TWAnySignerTests.cpp similarity index 97% rename from tests/Aion/TWAnySignerTests.cpp rename to tests/chains/Aion/TWAnySignerTests.cpp index 2f7ab4630c0..5b7f3417014 100644 --- a/tests/Aion/TWAnySignerTests.cpp +++ b/tests/chains/Aion/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include "proto/Aion.pb.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Aion::tests { diff --git a/tests/Aion/TWCoinTypeTests.cpp b/tests/chains/Aion/TWCoinTypeTests.cpp similarity index 97% rename from tests/Aion/TWCoinTypeTests.cpp rename to tests/chains/Aion/TWCoinTypeTests.cpp index e87d983d8d8..9d217c0654f 100644 --- a/tests/Aion/TWCoinTypeTests.cpp +++ b/tests/chains/Aion/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aion/TransactionTests.cpp b/tests/chains/Aion/TransactionTests.cpp similarity index 100% rename from tests/Aion/TransactionTests.cpp rename to tests/chains/Aion/TransactionTests.cpp diff --git a/tests/Algorand/AddressTests.cpp b/tests/chains/Algorand/AddressTests.cpp similarity index 100% rename from tests/Algorand/AddressTests.cpp rename to tests/chains/Algorand/AddressTests.cpp diff --git a/tests/Algorand/SignerTests.cpp b/tests/chains/Algorand/SignerTests.cpp similarity index 100% rename from tests/Algorand/SignerTests.cpp rename to tests/chains/Algorand/SignerTests.cpp diff --git a/tests/Algorand/TWAnySignerTests.cpp b/tests/chains/Algorand/TWAnySignerTests.cpp similarity index 98% rename from tests/Algorand/TWAnySignerTests.cpp rename to tests/chains/Algorand/TWAnySignerTests.cpp index 5b3e00aa411..3890bafcfeb 100644 --- a/tests/Algorand/TWAnySignerTests.cpp +++ b/tests/chains/Algorand/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include "proto/Algorand.pb.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Algorand/TWCoinTypeTests.cpp b/tests/chains/Algorand/TWCoinTypeTests.cpp similarity index 97% rename from tests/Algorand/TWCoinTypeTests.cpp rename to tests/chains/Algorand/TWCoinTypeTests.cpp index f95721fa727..26d33c3670f 100644 --- a/tests/Algorand/TWCoinTypeTests.cpp +++ b/tests/chains/Algorand/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aptos/AddressTests.cpp b/tests/chains/Aptos/AddressTests.cpp similarity index 100% rename from tests/Aptos/AddressTests.cpp rename to tests/chains/Aptos/AddressTests.cpp diff --git a/tests/Aptos/MoveTypesTests.cpp b/tests/chains/Aptos/MoveTypesTests.cpp similarity index 100% rename from tests/Aptos/MoveTypesTests.cpp rename to tests/chains/Aptos/MoveTypesTests.cpp diff --git a/tests/Aptos/SignerTests.cpp b/tests/chains/Aptos/SignerTests.cpp similarity index 99% rename from tests/Aptos/SignerTests.cpp rename to tests/chains/Aptos/SignerTests.cpp index 1ebb97cfcc3..3e0145d677b 100644 --- a/tests/Aptos/SignerTests.cpp +++ b/tests/chains/Aptos/SignerTests.cpp @@ -9,7 +9,7 @@ #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aptos/TWAnySignerTests.cpp b/tests/chains/Aptos/TWAnySignerTests.cpp similarity index 98% rename from tests/Aptos/TWAnySignerTests.cpp rename to tests/chains/Aptos/TWAnySignerTests.cpp index 1caf524cc5c..e42890edf7b 100644 --- a/tests/Aptos/TWAnySignerTests.cpp +++ b/tests/chains/Aptos/TWAnySignerTests.cpp @@ -11,7 +11,7 @@ #include "PublicKey.h" #include #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/Aptos/TWAptosAddressTests.cpp b/tests/chains/Aptos/TWAptosAddressTests.cpp similarity index 96% rename from tests/Aptos/TWAptosAddressTests.cpp rename to tests/chains/Aptos/TWAptosAddressTests.cpp index cc827933612..e03407f5c56 100644 --- a/tests/Aptos/TWAptosAddressTests.cpp +++ b/tests/chains/Aptos/TWAptosAddressTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aptos/TWCoinTypeTests.cpp b/tests/chains/Aptos/TWCoinTypeTests.cpp similarity index 97% rename from tests/Aptos/TWCoinTypeTests.cpp rename to tests/chains/Aptos/TWCoinTypeTests.cpp index e9f26f59987..af10a478945 100644 --- a/tests/Aptos/TWCoinTypeTests.cpp +++ b/tests/chains/Aptos/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aptos/TransactionPayloadTests.cpp b/tests/chains/Aptos/TransactionPayloadTests.cpp similarity index 100% rename from tests/Aptos/TransactionPayloadTests.cpp rename to tests/chains/Aptos/TransactionPayloadTests.cpp diff --git a/tests/Arbitrum/TWCoinTypeTests.cpp b/tests/chains/Arbitrum/TWCoinTypeTests.cpp similarity index 97% rename from tests/Arbitrum/TWCoinTypeTests.cpp rename to tests/chains/Arbitrum/TWCoinTypeTests.cpp index 35c6faa4ab6..bab96f946ff 100644 --- a/tests/Arbitrum/TWCoinTypeTests.cpp +++ b/tests/chains/Arbitrum/TWCoinTypeTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Aurora/TWCoinTypeTests.cpp b/tests/chains/Aurora/TWCoinTypeTests.cpp similarity index 97% rename from tests/Aurora/TWCoinTypeTests.cpp rename to tests/chains/Aurora/TWCoinTypeTests.cpp index 5dff7ccd8fb..b612ce508ce 100644 --- a/tests/Aurora/TWCoinTypeTests.cpp +++ b/tests/chains/Aurora/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Avalanche/TWCoinTypeTests.cpp b/tests/chains/Avalanche/TWCoinTypeTests.cpp similarity index 97% rename from tests/Avalanche/TWCoinTypeTests.cpp rename to tests/chains/Avalanche/TWCoinTypeTests.cpp index f9b1aa72576..8bf54001455 100644 --- a/tests/Avalanche/TWCoinTypeTests.cpp +++ b/tests/chains/Avalanche/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/BandChain/TWCoinTypeTests.cpp b/tests/chains/BandChain/TWCoinTypeTests.cpp similarity index 97% rename from tests/BandChain/TWCoinTypeTests.cpp rename to tests/chains/BandChain/TWCoinTypeTests.cpp index 4fdd7e8f90b..f6ad4c690c0 100644 --- a/tests/BandChain/TWCoinTypeTests.cpp +++ b/tests/chains/BandChain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Binance/SignerTests.cpp b/tests/chains/Binance/SignerTests.cpp similarity index 100% rename from tests/Binance/SignerTests.cpp rename to tests/chains/Binance/SignerTests.cpp diff --git a/tests/Binance/TWAnySignerTests.cpp b/tests/chains/Binance/TWAnySignerTests.cpp similarity index 99% rename from tests/Binance/TWAnySignerTests.cpp rename to tests/chains/Binance/TWAnySignerTests.cpp index aa06a82e55e..84193da844a 100644 --- a/tests/Binance/TWAnySignerTests.cpp +++ b/tests/chains/Binance/TWAnySignerTests.cpp @@ -10,7 +10,7 @@ #include #include "Coin.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Binance/TWCoinTypeTests.cpp b/tests/chains/Binance/TWCoinTypeTests.cpp similarity index 97% rename from tests/Binance/TWCoinTypeTests.cpp rename to tests/chains/Binance/TWCoinTypeTests.cpp index 52ce9bc3bf9..2059c040c6b 100644 --- a/tests/Binance/TWCoinTypeTests.cpp +++ b/tests/chains/Binance/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/BinanceSmartChain/SignerTests.cpp b/tests/chains/BinanceSmartChain/SignerTests.cpp similarity index 98% rename from tests/BinanceSmartChain/SignerTests.cpp rename to tests/chains/BinanceSmartChain/SignerTests.cpp index 3ec01b4f1e6..6303410de22 100644 --- a/tests/BinanceSmartChain/SignerTests.cpp +++ b/tests/chains/BinanceSmartChain/SignerTests.cpp @@ -12,7 +12,7 @@ #include "proto/Ethereum.pb.h" #include "HexCoding.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/BinanceSmartChain/TWAnyAddressTests.cpp b/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp similarity index 96% rename from tests/BinanceSmartChain/TWAnyAddressTests.cpp rename to tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp index e50537d7b8c..f7769d5c198 100644 --- a/tests/BinanceSmartChain/TWAnyAddressTests.cpp +++ b/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp @@ -7,7 +7,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/BinanceSmartChain/TWCoinTypeTests.cpp b/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp similarity index 98% rename from tests/BinanceSmartChain/TWCoinTypeTests.cpp rename to tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp index 9f768216878..f48a6b1e56e 100644 --- a/tests/BinanceSmartChain/TWCoinTypeTests.cpp +++ b/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Bitcoin/BitcoinAddressTests.cpp b/tests/chains/Bitcoin/BitcoinAddressTests.cpp similarity index 100% rename from tests/Bitcoin/BitcoinAddressTests.cpp rename to tests/chains/Bitcoin/BitcoinAddressTests.cpp diff --git a/tests/Bitcoin/BitcoinScriptTests.cpp b/tests/chains/Bitcoin/BitcoinScriptTests.cpp similarity index 99% rename from tests/Bitcoin/BitcoinScriptTests.cpp rename to tests/chains/Bitcoin/BitcoinScriptTests.cpp index 15d3cf7fc5e..66c50b9701e 100644 --- a/tests/Bitcoin/BitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/BitcoinScriptTests.cpp @@ -6,7 +6,7 @@ #include "Bitcoin/Script.h" #include "Bitcoin/SignatureBuilder.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include diff --git a/tests/Bitcoin/FeeCalculatorTests.cpp b/tests/chains/Bitcoin/FeeCalculatorTests.cpp similarity index 100% rename from tests/Bitcoin/FeeCalculatorTests.cpp rename to tests/chains/Bitcoin/FeeCalculatorTests.cpp diff --git a/tests/Bitcoin/InputSelectorTests.cpp b/tests/chains/Bitcoin/InputSelectorTests.cpp similarity index 100% rename from tests/Bitcoin/InputSelectorTests.cpp rename to tests/chains/Bitcoin/InputSelectorTests.cpp diff --git a/tests/Bitcoin/MessageSignerTests.cpp b/tests/chains/Bitcoin/MessageSignerTests.cpp similarity index 99% rename from tests/Bitcoin/MessageSignerTests.cpp rename to tests/chains/Bitcoin/MessageSignerTests.cpp index e8df4e56631..5ec7d6d361a 100644 --- a/tests/Bitcoin/MessageSignerTests.cpp +++ b/tests/chains/Bitcoin/MessageSignerTests.cpp @@ -19,7 +19,6 @@ #include #include -#include // TODO remove namespace TW::Bitcoin::MessageSignerTests { diff --git a/tests/Bitcoin/SegwitAddressTests.cpp b/tests/chains/Bitcoin/SegwitAddressTests.cpp similarity index 100% rename from tests/Bitcoin/SegwitAddressTests.cpp rename to tests/chains/Bitcoin/SegwitAddressTests.cpp diff --git a/tests/Bitcoin/TWBitcoinAddressTests.cpp b/tests/chains/Bitcoin/TWBitcoinAddressTests.cpp similarity index 98% rename from tests/Bitcoin/TWBitcoinAddressTests.cpp rename to tests/chains/Bitcoin/TWBitcoinAddressTests.cpp index c2e81b6d33a..13c96dc5a5c 100644 --- a/tests/Bitcoin/TWBitcoinAddressTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Bitcoin/TWBitcoinScriptTests.cpp b/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp similarity index 99% rename from tests/Bitcoin/TWBitcoinScriptTests.cpp rename to tests/chains/Bitcoin/TWBitcoinScriptTests.cpp index 064b83f50d9..2f9a3e1ad29 100644 --- a/tests/Bitcoin/TWBitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp similarity index 100% rename from tests/Bitcoin/TWBitcoinSigningTests.cpp rename to tests/chains/Bitcoin/TWBitcoinSigningTests.cpp diff --git a/tests/Bitcoin/TWBitcoinTransactionTests.cpp b/tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp similarity index 100% rename from tests/Bitcoin/TWBitcoinTransactionTests.cpp rename to tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp diff --git a/tests/Bitcoin/TWCoinTypeTests.cpp b/tests/chains/Bitcoin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Bitcoin/TWCoinTypeTests.cpp rename to tests/chains/Bitcoin/TWCoinTypeTests.cpp index 0cba7d8cb7e..3ce30dd594b 100644 --- a/tests/Bitcoin/TWCoinTypeTests.cpp +++ b/tests/chains/Bitcoin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Bitcoin/TWSegwitAddressTests.cpp b/tests/chains/Bitcoin/TWSegwitAddressTests.cpp similarity index 98% rename from tests/Bitcoin/TWSegwitAddressTests.cpp rename to tests/chains/Bitcoin/TWSegwitAddressTests.cpp index ccc41fc5737..65c632ca6b9 100644 --- a/tests/Bitcoin/TWSegwitAddressTests.cpp +++ b/tests/chains/Bitcoin/TWSegwitAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Bitcoin/TransactionPlanTests.cpp b/tests/chains/Bitcoin/TransactionPlanTests.cpp similarity index 100% rename from tests/Bitcoin/TransactionPlanTests.cpp rename to tests/chains/Bitcoin/TransactionPlanTests.cpp diff --git a/tests/Bitcoin/TxComparisonHelper.cpp b/tests/chains/Bitcoin/TxComparisonHelper.cpp similarity index 100% rename from tests/Bitcoin/TxComparisonHelper.cpp rename to tests/chains/Bitcoin/TxComparisonHelper.cpp diff --git a/tests/Bitcoin/TxComparisonHelper.h b/tests/chains/Bitcoin/TxComparisonHelper.h similarity index 100% rename from tests/Bitcoin/TxComparisonHelper.h rename to tests/chains/Bitcoin/TxComparisonHelper.h diff --git a/tests/BitcoinCash/TWBitcoinCashTests.cpp b/tests/chains/BitcoinCash/TWBitcoinCashTests.cpp similarity index 99% rename from tests/BitcoinCash/TWBitcoinCashTests.cpp rename to tests/chains/BitcoinCash/TWBitcoinCashTests.cpp index e4f960149f6..19f6fc564d4 100644 --- a/tests/BitcoinCash/TWBitcoinCashTests.cpp +++ b/tests/chains/BitcoinCash/TWBitcoinCashTests.cpp @@ -8,7 +8,7 @@ #include "Bitcoin/SigHashType.h" #include "HexCoding.h" #include "proto/Bitcoin.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/BitcoinCash/TWCoinTypeTests.cpp b/tests/chains/BitcoinCash/TWCoinTypeTests.cpp similarity index 97% rename from tests/BitcoinCash/TWCoinTypeTests.cpp rename to tests/chains/BitcoinCash/TWCoinTypeTests.cpp index 9b56e10a7af..666aa70328c 100644 --- a/tests/BitcoinCash/TWCoinTypeTests.cpp +++ b/tests/chains/BitcoinCash/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/BitcoinGold/TWAddressTests.cpp b/tests/chains/BitcoinGold/TWAddressTests.cpp similarity index 96% rename from tests/BitcoinGold/TWAddressTests.cpp rename to tests/chains/BitcoinGold/TWAddressTests.cpp index 77c192461bb..977c77900e1 100644 --- a/tests/BitcoinGold/TWAddressTests.cpp +++ b/tests/chains/BitcoinGold/TWAddressTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/Address.h" #include "PrivateKey.h" #include "PublicKey.h" diff --git a/tests/BitcoinGold/TWBitcoinGoldTests.cpp b/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp similarity index 99% rename from tests/BitcoinGold/TWBitcoinGoldTests.cpp rename to tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp index 6c4247b4f90..760cd79b9a6 100644 --- a/tests/BitcoinGold/TWBitcoinGoldTests.cpp +++ b/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp @@ -20,7 +20,7 @@ #include "Bitcoin/TransactionSigner.h" #include "HexCoding.h" #include "proto/Bitcoin.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" using namespace TW; diff --git a/tests/BitcoinGold/TWCoinTypeTests.cpp b/tests/chains/BitcoinGold/TWCoinTypeTests.cpp similarity index 97% rename from tests/BitcoinGold/TWCoinTypeTests.cpp rename to tests/chains/BitcoinGold/TWCoinTypeTests.cpp index 55b11cdfa52..5ab3db7a03b 100644 --- a/tests/BitcoinGold/TWCoinTypeTests.cpp +++ b/tests/chains/BitcoinGold/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/BitcoinGold/TWSegwitAddressTests.cpp b/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp similarity index 98% rename from tests/BitcoinGold/TWSegwitAddressTests.cpp rename to tests/chains/BitcoinGold/TWSegwitAddressTests.cpp index b526c21a480..0f8c1413fe1 100644 --- a/tests/BitcoinGold/TWSegwitAddressTests.cpp +++ b/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/SegwitAddress.h" #include "Coin.h" #include "PrivateKey.h" diff --git a/tests/BitcoinGold/TWSignerTests.cpp b/tests/chains/BitcoinGold/TWSignerTests.cpp similarity index 98% rename from tests/BitcoinGold/TWSignerTests.cpp rename to tests/chains/BitcoinGold/TWSignerTests.cpp index f75bc5219a5..673e6437912 100644 --- a/tests/BitcoinGold/TWSignerTests.cpp +++ b/tests/chains/BitcoinGold/TWSignerTests.cpp @@ -18,7 +18,7 @@ #include "HexCoding.h" #include "proto/Bitcoin.pb.h" #include "../Bitcoin/TxComparisonHelper.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" namespace TW::Bitcoin { diff --git a/tests/Bluzelle/TWCoinTypeTests.cpp b/tests/chains/Bluzelle/TWCoinTypeTests.cpp similarity index 97% rename from tests/Bluzelle/TWCoinTypeTests.cpp rename to tests/chains/Bluzelle/TWCoinTypeTests.cpp index 33acfa33f7b..09dcb1aed7b 100644 --- a/tests/Bluzelle/TWCoinTypeTests.cpp +++ b/tests/chains/Bluzelle/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Boba/TWCoinTypeTests.cpp b/tests/chains/Boba/TWCoinTypeTests.cpp similarity index 97% rename from tests/Boba/TWCoinTypeTests.cpp rename to tests/chains/Boba/TWCoinTypeTests.cpp index d538cea0cf3..9d9d27f52cf 100644 --- a/tests/Boba/TWCoinTypeTests.cpp +++ b/tests/chains/Boba/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Callisto/TWCoinTypeTests.cpp b/tests/chains/Callisto/TWCoinTypeTests.cpp similarity index 97% rename from tests/Callisto/TWCoinTypeTests.cpp rename to tests/chains/Callisto/TWCoinTypeTests.cpp index 2c83326e867..e742718974f 100644 --- a/tests/Callisto/TWCoinTypeTests.cpp +++ b/tests/chains/Callisto/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cardano/AddressTests.cpp b/tests/chains/Cardano/AddressTests.cpp similarity index 100% rename from tests/Cardano/AddressTests.cpp rename to tests/chains/Cardano/AddressTests.cpp diff --git a/tests/Cardano/SigningTests.cpp b/tests/chains/Cardano/SigningTests.cpp similarity index 99% rename from tests/Cardano/SigningTests.cpp rename to tests/chains/Cardano/SigningTests.cpp index d0fb95a5a95..79b1fe0072c 100644 --- a/tests/Cardano/SigningTests.cpp +++ b/tests/chains/Cardano/SigningTests.cpp @@ -13,7 +13,7 @@ #include "HexCoding.h" #include "PrivateKey.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cardano/StakingTests.cpp b/tests/chains/Cardano/StakingTests.cpp similarity index 99% rename from tests/Cardano/StakingTests.cpp rename to tests/chains/Cardano/StakingTests.cpp index a1cc13e71f1..cf7636b23c5 100644 --- a/tests/Cardano/StakingTests.cpp +++ b/tests/chains/Cardano/StakingTests.cpp @@ -13,7 +13,7 @@ #include "HexCoding.h" #include "PrivateKey.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cardano/TWCardanoAddressTests.cpp b/tests/chains/Cardano/TWCardanoAddressTests.cpp similarity index 99% rename from tests/Cardano/TWCardanoAddressTests.cpp rename to tests/chains/Cardano/TWCardanoAddressTests.cpp index e77f3e81520..709cba08745 100644 --- a/tests/Cardano/TWCardanoAddressTests.cpp +++ b/tests/chains/Cardano/TWCardanoAddressTests.cpp @@ -12,7 +12,7 @@ #include #include #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "PrivateKey.h" #include diff --git a/tests/Cardano/TWCoinTypeTests.cpp b/tests/chains/Cardano/TWCoinTypeTests.cpp similarity index 97% rename from tests/Cardano/TWCoinTypeTests.cpp rename to tests/chains/Cardano/TWCoinTypeTests.cpp index 987f2fc7856..33f8c9e197d 100644 --- a/tests/Cardano/TWCoinTypeTests.cpp +++ b/tests/chains/Cardano/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cardano/TransactionTests.cpp b/tests/chains/Cardano/TransactionTests.cpp similarity index 100% rename from tests/Cardano/TransactionTests.cpp rename to tests/chains/Cardano/TransactionTests.cpp diff --git a/tests/Celo/TWCoinTypeTests.cpp b/tests/chains/Celo/TWCoinTypeTests.cpp similarity index 97% rename from tests/Celo/TWCoinTypeTests.cpp rename to tests/chains/Celo/TWCoinTypeTests.cpp index 7a66fc66eda..35fd5a76d73 100644 --- a/tests/Celo/TWCoinTypeTests.cpp +++ b/tests/chains/Celo/TWCoinTypeTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cosmos/AddressTests.cpp b/tests/chains/Cosmos/AddressTests.cpp similarity index 100% rename from tests/Cosmos/AddressTests.cpp rename to tests/chains/Cosmos/AddressTests.cpp diff --git a/tests/Cosmos/Protobuf/.gitignore b/tests/chains/Cosmos/Protobuf/.gitignore similarity index 100% rename from tests/Cosmos/Protobuf/.gitignore rename to tests/chains/Cosmos/Protobuf/.gitignore diff --git a/tests/Cosmos/Protobuf/Article.proto b/tests/chains/Cosmos/Protobuf/Article.proto similarity index 100% rename from tests/Cosmos/Protobuf/Article.proto rename to tests/chains/Cosmos/Protobuf/Article.proto diff --git a/tests/Cosmos/ProtobufTests.cpp b/tests/chains/Cosmos/ProtobufTests.cpp similarity index 98% rename from tests/Cosmos/ProtobufTests.cpp rename to tests/chains/Cosmos/ProtobufTests.cpp index 73557fd4303..89b36346ac5 100644 --- a/tests/Cosmos/ProtobufTests.cpp +++ b/tests/chains/Cosmos/ProtobufTests.cpp @@ -13,7 +13,7 @@ #include "HexCoding.h" #include "Protobuf/Article.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cosmos/SignerTests.cpp b/tests/chains/Cosmos/SignerTests.cpp similarity index 99% rename from tests/Cosmos/SignerTests.cpp rename to tests/chains/Cosmos/SignerTests.cpp index 36355e3ed4f..a791e7bd10f 100644 --- a/tests/Cosmos/SignerTests.cpp +++ b/tests/chains/Cosmos/SignerTests.cpp @@ -10,7 +10,7 @@ #include "proto/Cosmos.pb.h" #include "Cosmos/Address.h" #include "Cosmos/Signer.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Cosmos/Protobuf/bank_tx.pb.h" #include diff --git a/tests/Cosmos/StakingTests.cpp b/tests/chains/Cosmos/StakingTests.cpp similarity index 99% rename from tests/Cosmos/StakingTests.cpp rename to tests/chains/Cosmos/StakingTests.cpp index f3fb89a34f5..0abee814e66 100644 --- a/tests/Cosmos/StakingTests.cpp +++ b/tests/chains/Cosmos/StakingTests.cpp @@ -10,7 +10,7 @@ #include "Cosmos/Signer.h" #include "HexCoding.h" #include "proto/Cosmos.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Cosmos::tests { diff --git a/tests/Cosmos/TWAnyAddressTests.cpp b/tests/chains/Cosmos/TWAnyAddressTests.cpp similarity index 95% rename from tests/Cosmos/TWAnyAddressTests.cpp rename to tests/chains/Cosmos/TWAnyAddressTests.cpp index 5605e54aef0..27559eff580 100644 --- a/tests/Cosmos/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/TWAnyAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cosmos/TWAnySignerTests.cpp b/tests/chains/Cosmos/TWAnySignerTests.cpp similarity index 98% rename from tests/Cosmos/TWAnySignerTests.cpp rename to tests/chains/Cosmos/TWAnySignerTests.cpp index ae6fd1e8bf7..014bd26d4f4 100644 --- a/tests/Cosmos/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/TWAnySignerTests.cpp @@ -7,7 +7,7 @@ #include "Cosmos/Address.h" #include "HexCoding.h" #include "proto/Cosmos.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cosmos/TWCoinTypeTests.cpp b/tests/chains/Cosmos/TWCoinTypeTests.cpp similarity index 97% rename from tests/Cosmos/TWCoinTypeTests.cpp rename to tests/chains/Cosmos/TWCoinTypeTests.cpp index 10d3a2d3cc4..228564d05d3 100644 --- a/tests/Cosmos/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cronos/TWAnyAddressTests.cpp b/tests/chains/Cronos/TWAnyAddressTests.cpp similarity index 94% rename from tests/Cronos/TWAnyAddressTests.cpp rename to tests/chains/Cronos/TWAnyAddressTests.cpp index bee3a617641..115c061283f 100644 --- a/tests/Cronos/TWAnyAddressTests.cpp +++ b/tests/chains/Cronos/TWAnyAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Cronos/TWCoinTypeTests.cpp b/tests/chains/Cronos/TWCoinTypeTests.cpp similarity index 97% rename from tests/Cronos/TWCoinTypeTests.cpp rename to tests/chains/Cronos/TWCoinTypeTests.cpp index 728c15a121d..b3f60a93b58 100644 --- a/tests/Cronos/TWCoinTypeTests.cpp +++ b/tests/chains/Cronos/TWCoinTypeTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/CryptoOrg/AddressTests.cpp b/tests/chains/CryptoOrg/AddressTests.cpp similarity index 100% rename from tests/CryptoOrg/AddressTests.cpp rename to tests/chains/CryptoOrg/AddressTests.cpp diff --git a/tests/CryptoOrg/SignerTests.cpp b/tests/chains/CryptoOrg/SignerTests.cpp similarity index 99% rename from tests/CryptoOrg/SignerTests.cpp rename to tests/chains/CryptoOrg/SignerTests.cpp index 9cb39723edb..bfc9dc634b5 100644 --- a/tests/CryptoOrg/SignerTests.cpp +++ b/tests/chains/CryptoOrg/SignerTests.cpp @@ -9,7 +9,7 @@ #include "Cosmos/Address.h" #include "HexCoding.h" #include "PublicKey.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/CryptoOrg/TWAnyAddressTests.cpp b/tests/chains/CryptoOrg/TWAnyAddressTests.cpp similarity index 96% rename from tests/CryptoOrg/TWAnyAddressTests.cpp rename to tests/chains/CryptoOrg/TWAnyAddressTests.cpp index e9771fed810..4af5812a464 100644 --- a/tests/CryptoOrg/TWAnyAddressTests.cpp +++ b/tests/chains/CryptoOrg/TWAnyAddressTests.cpp @@ -7,7 +7,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/CryptoOrg/TWAnySignerTests.cpp b/tests/chains/CryptoOrg/TWAnySignerTests.cpp similarity index 99% rename from tests/CryptoOrg/TWAnySignerTests.cpp rename to tests/chains/CryptoOrg/TWAnySignerTests.cpp index 5f87a527d21..62519f35671 100644 --- a/tests/CryptoOrg/TWAnySignerTests.cpp +++ b/tests/chains/CryptoOrg/TWAnySignerTests.cpp @@ -10,7 +10,7 @@ #include "Base64.h" #include "Data.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/CryptoOrg/TWCoinTypeTests.cpp b/tests/chains/CryptoOrg/TWCoinTypeTests.cpp similarity index 97% rename from tests/CryptoOrg/TWCoinTypeTests.cpp rename to tests/chains/CryptoOrg/TWCoinTypeTests.cpp index a6e06751ec4..0e19d390f0b 100644 --- a/tests/CryptoOrg/TWCoinTypeTests.cpp +++ b/tests/chains/CryptoOrg/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Dash/TWCoinTypeTests.cpp b/tests/chains/Dash/TWCoinTypeTests.cpp similarity index 97% rename from tests/Dash/TWCoinTypeTests.cpp rename to tests/chains/Dash/TWCoinTypeTests.cpp index 20fa3257b39..9995965fda6 100644 --- a/tests/Dash/TWCoinTypeTests.cpp +++ b/tests/chains/Dash/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Dash/TWDashTests.cpp b/tests/chains/Dash/TWDashTests.cpp similarity index 95% rename from tests/Dash/TWDashTests.cpp rename to tests/chains/Dash/TWDashTests.cpp index c1ad9c58117..a0d86239464 100644 --- a/tests/Dash/TWDashTests.cpp +++ b/tests/chains/Dash/TWDashTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/Decred/AddressTests.cpp b/tests/chains/Decred/AddressTests.cpp similarity index 100% rename from tests/Decred/AddressTests.cpp rename to tests/chains/Decred/AddressTests.cpp diff --git a/tests/Decred/SignerTests.cpp b/tests/chains/Decred/SignerTests.cpp similarity index 100% rename from tests/Decred/SignerTests.cpp rename to tests/chains/Decred/SignerTests.cpp diff --git a/tests/Decred/TWAnySignerTests.cpp b/tests/chains/Decred/TWAnySignerTests.cpp similarity index 99% rename from tests/Decred/TWAnySignerTests.cpp rename to tests/chains/Decred/TWAnySignerTests.cpp index 033d87a4a2c..2960a5fcb1c 100644 --- a/tests/Decred/TWAnySignerTests.cpp +++ b/tests/chains/Decred/TWAnySignerTests.cpp @@ -5,7 +5,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "proto/Bitcoin.pb.h" diff --git a/tests/Decred/TWCoinTypeTests.cpp b/tests/chains/Decred/TWCoinTypeTests.cpp similarity index 97% rename from tests/Decred/TWCoinTypeTests.cpp rename to tests/chains/Decred/TWCoinTypeTests.cpp index 8e8200bae80..f355997d41b 100644 --- a/tests/Decred/TWCoinTypeTests.cpp +++ b/tests/chains/Decred/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Decred/TWDecredTests.cpp b/tests/chains/Decred/TWDecredTests.cpp similarity index 98% rename from tests/Decred/TWDecredTests.cpp rename to tests/chains/Decred/TWDecredTests.cpp index 1b05944929b..661420c887c 100644 --- a/tests/Decred/TWDecredTests.cpp +++ b/tests/chains/Decred/TWDecredTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/DigiByte/TWCoinTypeTests.cpp b/tests/chains/DigiByte/TWCoinTypeTests.cpp similarity index 97% rename from tests/DigiByte/TWCoinTypeTests.cpp rename to tests/chains/DigiByte/TWCoinTypeTests.cpp index 0140cef4893..46d2adfa5de 100644 --- a/tests/DigiByte/TWCoinTypeTests.cpp +++ b/tests/chains/DigiByte/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/DigiByte/TWDigiByteTests.cpp b/tests/chains/DigiByte/TWDigiByteTests.cpp similarity index 99% rename from tests/DigiByte/TWDigiByteTests.cpp rename to tests/chains/DigiByte/TWDigiByteTests.cpp index 32ff866d846..c824018607c 100644 --- a/tests/DigiByte/TWDigiByteTests.cpp +++ b/tests/chains/DigiByte/TWDigiByteTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/TransactionBuilder.h" diff --git a/tests/Dogecoin/TWCoinTypeTests.cpp b/tests/chains/Dogecoin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Dogecoin/TWCoinTypeTests.cpp rename to tests/chains/Dogecoin/TWCoinTypeTests.cpp index decba7e836e..3196965f758 100644 --- a/tests/Dogecoin/TWCoinTypeTests.cpp +++ b/tests/chains/Dogecoin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Dogecoin/TWDogeTests.cpp b/tests/chains/Dogecoin/TWDogeTests.cpp similarity index 95% rename from tests/Dogecoin/TWDogeTests.cpp rename to tests/chains/Dogecoin/TWDogeTests.cpp index 76f83bdb96a..627fc78a72e 100644 --- a/tests/Dogecoin/TWDogeTests.cpp +++ b/tests/chains/Dogecoin/TWDogeTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/ECO/TWCoinTypeTests.cpp b/tests/chains/ECO/TWCoinTypeTests.cpp similarity index 97% rename from tests/ECO/TWCoinTypeTests.cpp rename to tests/chains/ECO/TWCoinTypeTests.cpp index 3f2f2ae6b96..a72cb7b7877 100644 --- a/tests/ECO/TWCoinTypeTests.cpp +++ b/tests/chains/ECO/TWCoinTypeTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/ECash/TWCoinTypeTests.cpp b/tests/chains/ECash/TWCoinTypeTests.cpp similarity index 97% rename from tests/ECash/TWCoinTypeTests.cpp rename to tests/chains/ECash/TWCoinTypeTests.cpp index 59d2ea5954d..d8f9305bc12 100644 --- a/tests/ECash/TWCoinTypeTests.cpp +++ b/tests/chains/ECash/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/ECash/TWECashTests.cpp b/tests/chains/ECash/TWECashTests.cpp similarity index 99% rename from tests/ECash/TWECashTests.cpp rename to tests/chains/ECash/TWECashTests.cpp index 974f9bcff81..f258e4a4816 100644 --- a/tests/ECash/TWECashTests.cpp +++ b/tests/chains/ECash/TWECashTests.cpp @@ -8,7 +8,7 @@ #include "Bitcoin/SigHashType.h" #include "HexCoding.h" #include "proto/Bitcoin.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/EOS/AddressTests.cpp b/tests/chains/EOS/AddressTests.cpp similarity index 100% rename from tests/EOS/AddressTests.cpp rename to tests/chains/EOS/AddressTests.cpp diff --git a/tests/EOS/AssetTests.cpp b/tests/chains/EOS/AssetTests.cpp similarity index 100% rename from tests/EOS/AssetTests.cpp rename to tests/chains/EOS/AssetTests.cpp diff --git a/tests/EOS/NameTests.cpp b/tests/chains/EOS/NameTests.cpp similarity index 100% rename from tests/EOS/NameTests.cpp rename to tests/chains/EOS/NameTests.cpp diff --git a/tests/EOS/SignatureTests.cpp b/tests/chains/EOS/SignatureTests.cpp similarity index 100% rename from tests/EOS/SignatureTests.cpp rename to tests/chains/EOS/SignatureTests.cpp diff --git a/tests/EOS/TWAnySignerTests.cpp b/tests/chains/EOS/TWAnySignerTests.cpp similarity index 98% rename from tests/EOS/TWAnySignerTests.cpp rename to tests/chains/EOS/TWAnySignerTests.cpp index ea9e2b2b237..e075c7ad8d8 100644 --- a/tests/EOS/TWAnySignerTests.cpp +++ b/tests/chains/EOS/TWAnySignerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include "HexCoding.h" #include "proto/EOS.pb.h" diff --git a/tests/EOS/TWCoinTypeTests.cpp b/tests/chains/EOS/TWCoinTypeTests.cpp similarity index 97% rename from tests/EOS/TWCoinTypeTests.cpp rename to tests/chains/EOS/TWCoinTypeTests.cpp index 65412f55772..18edd68aa46 100644 --- a/tests/EOS/TWCoinTypeTests.cpp +++ b/tests/chains/EOS/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/EOS/TransactionTests.cpp b/tests/chains/EOS/TransactionTests.cpp similarity index 100% rename from tests/EOS/TransactionTests.cpp rename to tests/chains/EOS/TransactionTests.cpp diff --git a/tests/Elrond/AddressTests.cpp b/tests/chains/Elrond/AddressTests.cpp similarity index 100% rename from tests/Elrond/AddressTests.cpp rename to tests/chains/Elrond/AddressTests.cpp diff --git a/tests/Elrond/SerializationTests.cpp b/tests/chains/Elrond/SerializationTests.cpp similarity index 100% rename from tests/Elrond/SerializationTests.cpp rename to tests/chains/Elrond/SerializationTests.cpp diff --git a/tests/Elrond/SignerTests.cpp b/tests/chains/Elrond/SignerTests.cpp similarity index 100% rename from tests/Elrond/SignerTests.cpp rename to tests/chains/Elrond/SignerTests.cpp diff --git a/tests/Elrond/TWAnySignerTests.cpp b/tests/chains/Elrond/TWAnySignerTests.cpp similarity index 98% rename from tests/Elrond/TWAnySignerTests.cpp rename to tests/chains/Elrond/TWAnySignerTests.cpp index 277864dbe9e..09785661191 100644 --- a/tests/Elrond/TWAnySignerTests.cpp +++ b/tests/chains/Elrond/TWAnySignerTests.cpp @@ -10,7 +10,7 @@ #include "Elrond/Signer.h" #include "HexCoding.h" #include "TestAccounts.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Elrond/TWCoinTypeTests.cpp b/tests/chains/Elrond/TWCoinTypeTests.cpp similarity index 97% rename from tests/Elrond/TWCoinTypeTests.cpp rename to tests/chains/Elrond/TWCoinTypeTests.cpp index 900d36abd6d..1f7a977bdc0 100644 --- a/tests/Elrond/TWCoinTypeTests.cpp +++ b/tests/chains/Elrond/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Elrond/TestAccounts.h b/tests/chains/Elrond/TestAccounts.h similarity index 100% rename from tests/Elrond/TestAccounts.h rename to tests/chains/Elrond/TestAccounts.h diff --git a/tests/Elrond/TransactionFactoryTests.cpp b/tests/chains/Elrond/TransactionFactoryTests.cpp similarity index 100% rename from tests/Elrond/TransactionFactoryTests.cpp rename to tests/chains/Elrond/TransactionFactoryTests.cpp diff --git a/tests/Ethereum/AbiStructTests.cpp b/tests/chains/Ethereum/AbiStructTests.cpp similarity index 99% rename from tests/Ethereum/AbiStructTests.cpp rename to tests/chains/Ethereum/AbiStructTests.cpp index 7db3e83333b..259fb4ef3f6 100644 --- a/tests/Ethereum/AbiStructTests.cpp +++ b/tests/chains/Ethereum/AbiStructTests.cpp @@ -7,7 +7,7 @@ #include "Ethereum/ABI.h" #include "Ethereum/Address.h" #include "Ethereum/Signer.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include @@ -487,7 +487,7 @@ TEST(EthereumAbiStruct, hashStructJson) { } TEST(EthereumAbiStruct, hashStruct_emptyString) { - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_emptyString.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_emptyString.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "bc9d33285c5e42b00571f5deaf9636d2e498a6fa50e0d1be81095bded070117a"); @@ -500,7 +500,7 @@ TEST(EthereumAbiStruct, hashStruct_emptyString) { } TEST(EthereumAbiStruct, hashStruct_emptyArray) { - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_emptyArray.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_emptyArray.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "9f1a1bc718e966d683c544aef6fd0b73c85a1d6244af9b64bb8f4a6fa6716086"); @@ -514,7 +514,7 @@ TEST(EthereumAbiStruct, hashStruct_emptyArray) { TEST(EthereumAbiStruct, hashStruct_walletConnect) { // https://github.com/WalletConnect/walletconnect-example-dapp/blob/master/src/helpers/eip712.ts - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_walletconnect.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_walletconnect.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "abc79f527273b9e7bca1b3f1ac6ad1a8431fa6dc34ece900deabcd6969856b5e"); @@ -527,7 +527,7 @@ TEST(EthereumAbiStruct, hashStruct_walletConnect) { } TEST(EthereumAbiStruct, hashStruct_cryptofights) { - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_cryptofights.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_cryptofights.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "db12328a6d193965801548e1174936c3aa7adbe1b54b3535a3c905bd4966467c"); @@ -540,7 +540,7 @@ TEST(EthereumAbiStruct, hashStruct_cryptofights) { } TEST(EthereumAbiStruct, hashStruct_rarible) { - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_rarible.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_rarible.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "df0200de55c05eb55af2597012767ea3af653d68000be49580f8e05acd91d366"); @@ -553,7 +553,7 @@ TEST(EthereumAbiStruct, hashStruct_rarible) { } TEST(EthereumAbiStruct, hashStruct_snapshot) { - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_snapshot_v4.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_snapshot_v4.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "f558d08ad4a7651dbc9ec028cfcb4a8e6878a249073ef4fa694f85ee95f61c0f"); diff --git a/tests/Ethereum/AbiTests.cpp b/tests/chains/Ethereum/AbiTests.cpp similarity index 99% rename from tests/Ethereum/AbiTests.cpp rename to tests/chains/Ethereum/AbiTests.cpp index 598fc454f86..3575dcb0f95 100644 --- a/tests/Ethereum/AbiTests.cpp +++ b/tests/chains/Ethereum/AbiTests.cpp @@ -6,7 +6,7 @@ #include "Ethereum/ABI.h" #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/Ethereum/AddressTests.cpp b/tests/chains/Ethereum/AddressTests.cpp similarity index 100% rename from tests/Ethereum/AddressTests.cpp rename to tests/chains/Ethereum/AddressTests.cpp diff --git a/tests/Ethereum/ContractCallTests.cpp b/tests/chains/Ethereum/ContractCallTests.cpp similarity index 95% rename from tests/Ethereum/ContractCallTests.cpp rename to tests/chains/Ethereum/ContractCallTests.cpp index f338581c242..bf21bcd21cb 100644 --- a/tests/Ethereum/ContractCallTests.cpp +++ b/tests/chains/Ethereum/ContractCallTests.cpp @@ -22,7 +22,7 @@ static nlohmann::json load_json(std::string path) { } TEST(ContractCall, Approval) { - auto path = TESTS_ROOT + "/Ethereum/Data/erc20.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/erc20.json"; auto abi = load_json(path); auto call = parse_hex("095ea7b30000000000000000000000005aaeb6053f3e94c9b9a09f33669435e7ef1beaed" "0000000000000000000000000000000000000000000000000000000000000001"); @@ -35,7 +35,7 @@ TEST(ContractCall, Approval) { } TEST(ContractCall, UniswapSwapTokens) { - auto path = TESTS_ROOT + "/Ethereum/Data/uniswap_router_v2.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/uniswap_router_v2.json"; auto abi = load_json(path); // https://etherscan.io/tx/0x57a2414f3cd9ca373b7e663ae67ecf933e40cb77a6e4ed28e4e28b5aa0d8ec63 auto call = parse_hex( @@ -55,7 +55,7 @@ TEST(ContractCall, UniswapSwapTokens) { } TEST(ContractCall, KyberTrade) { - auto path = TESTS_ROOT + "/Ethereum/Data/kyber_proxy.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/kyber_proxy.json"; auto abi = load_json(path); // https://etherscan.io/tx/0x51ffab782b9a27d754389505d5a50db525c04c68142ce20512d579f10f9e13e4 @@ -77,7 +77,7 @@ TEST(ContractCall, KyberTrade) { } TEST(ContractCall, ApprovalForAll) { - auto path = TESTS_ROOT + "/Ethereum/Data/erc721.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/erc721.json"; auto abi = load_json(path); // https://etherscan.io/tx/0xc2744000a107aee4761cf8a638657f91c3003a54e2f1818c37d781be7e48187a @@ -92,7 +92,7 @@ TEST(ContractCall, ApprovalForAll) { } TEST(ContractCall, CustomCall) { - auto path = TESTS_ROOT + "/Ethereum/Data/custom.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/custom.json"; auto abi = load_json(path); auto call = parse_hex("ec37a4a000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000067472757374790000000000000000000000000000000000000000000000000000"); @@ -106,7 +106,7 @@ TEST(ContractCall, CustomCall) { TEST(ContractCall, SetResolver) { auto call = parse_hex("0x1896f70ae71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c" "6f0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41"); - auto path = TESTS_ROOT + "/Ethereum/Data/ens.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; auto abi = load_json(path); auto decoded = decodeCall(call, abi); auto expected = @@ -120,7 +120,7 @@ TEST(ContractCall, RenewENS) { "0xacf1a84100000000000000000000000000000000000000000000000000000000000000400000000000000000" "000000000000000000000000000000000000000001e18558000000000000000000000000000000000000000000" "000000000000000000000a68657769676f76656e7300000000000000000000000000000000000000000000"); - auto path = TESTS_ROOT + "/Ethereum/Data/ens.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; auto abi = load_json(path); auto decoded = decodeCall(call, abi); auto expected = @@ -153,7 +153,7 @@ TEST(ContractCall, Multicall) { "000000000000000000000000000000000014d30f834b53d8f7e851e87b90ffa65757a35b850500000000000000" "000000000000000000000000000000000000000000000000000000000000000000"); ASSERT_EQ(4 + 928ul, call.size()); - auto path = TESTS_ROOT + "/Ethereum/Data/ens.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; auto abi = load_json(path); auto decoded = decodeCall(call, abi); auto expected = @@ -174,7 +174,7 @@ TEST(ContractCall, GetAmountsOut) { "0000000000000000000000000000000000000000000000000000000000000040" "0000000000000000000000000000000000000000000000000000000000000001" "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"); - auto path = TESTS_ROOT + "/Ethereum/Data/getAmountsOut.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/getAmountsOut.json"; auto abi = load_json(path); auto decoded = decodeCall(call, abi); @@ -185,7 +185,7 @@ TEST(ContractCall, GetAmountsOut) { } TEST(ContractCall, 1inch) { - auto path = TESTS_ROOT + "/Ethereum/Data/1inch.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/1inch.json"; auto abi = load_json(path); // https://etherscan.io/tx/0xc2d113151124579c21332d4cc6ab2b7f61e81d62392ed8596174513cb47e35ba @@ -199,7 +199,7 @@ TEST(ContractCall, 1inch) { } TEST(ContractCall, TupleNested) { - auto path = TESTS_ROOT + "/Ethereum/Data/tuple_nested.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/tuple_nested.json"; auto abi = load_json(path); auto call = parse_hex( diff --git a/tests/Ethereum/Data/1inch.json b/tests/chains/Ethereum/Data/1inch.json similarity index 100% rename from tests/Ethereum/Data/1inch.json rename to tests/chains/Ethereum/Data/1inch.json diff --git a/tests/Ethereum/Data/custom.json b/tests/chains/Ethereum/Data/custom.json similarity index 100% rename from tests/Ethereum/Data/custom.json rename to tests/chains/Ethereum/Data/custom.json diff --git a/tests/Ethereum/Data/eip712_cryptofights.json b/tests/chains/Ethereum/Data/eip712_cryptofights.json similarity index 100% rename from tests/Ethereum/Data/eip712_cryptofights.json rename to tests/chains/Ethereum/Data/eip712_cryptofights.json diff --git a/tests/Ethereum/Data/eip712_emptyArray.json b/tests/chains/Ethereum/Data/eip712_emptyArray.json similarity index 100% rename from tests/Ethereum/Data/eip712_emptyArray.json rename to tests/chains/Ethereum/Data/eip712_emptyArray.json diff --git a/tests/Ethereum/Data/eip712_emptyString.json b/tests/chains/Ethereum/Data/eip712_emptyString.json similarity index 100% rename from tests/Ethereum/Data/eip712_emptyString.json rename to tests/chains/Ethereum/Data/eip712_emptyString.json diff --git a/tests/Ethereum/Data/eip712_rarible.json b/tests/chains/Ethereum/Data/eip712_rarible.json similarity index 100% rename from tests/Ethereum/Data/eip712_rarible.json rename to tests/chains/Ethereum/Data/eip712_rarible.json diff --git a/tests/Ethereum/Data/eip712_snapshot_v4.json b/tests/chains/Ethereum/Data/eip712_snapshot_v4.json similarity index 100% rename from tests/Ethereum/Data/eip712_snapshot_v4.json rename to tests/chains/Ethereum/Data/eip712_snapshot_v4.json diff --git a/tests/Ethereum/Data/eip712_walletconnect.json b/tests/chains/Ethereum/Data/eip712_walletconnect.json similarity index 100% rename from tests/Ethereum/Data/eip712_walletconnect.json rename to tests/chains/Ethereum/Data/eip712_walletconnect.json diff --git a/tests/Ethereum/Data/ens.json b/tests/chains/Ethereum/Data/ens.json similarity index 100% rename from tests/Ethereum/Data/ens.json rename to tests/chains/Ethereum/Data/ens.json diff --git a/tests/Ethereum/Data/erc20.json b/tests/chains/Ethereum/Data/erc20.json similarity index 100% rename from tests/Ethereum/Data/erc20.json rename to tests/chains/Ethereum/Data/erc20.json diff --git a/tests/Ethereum/Data/erc721.json b/tests/chains/Ethereum/Data/erc721.json similarity index 100% rename from tests/Ethereum/Data/erc721.json rename to tests/chains/Ethereum/Data/erc721.json diff --git a/tests/Ethereum/Data/eth_feeHistory.json b/tests/chains/Ethereum/Data/eth_feeHistory.json similarity index 100% rename from tests/Ethereum/Data/eth_feeHistory.json rename to tests/chains/Ethereum/Data/eth_feeHistory.json diff --git a/tests/Ethereum/Data/eth_feeHistory2.json b/tests/chains/Ethereum/Data/eth_feeHistory2.json similarity index 100% rename from tests/Ethereum/Data/eth_feeHistory2.json rename to tests/chains/Ethereum/Data/eth_feeHistory2.json diff --git a/tests/Ethereum/Data/eth_feeHistory3.json b/tests/chains/Ethereum/Data/eth_feeHistory3.json similarity index 100% rename from tests/Ethereum/Data/eth_feeHistory3.json rename to tests/chains/Ethereum/Data/eth_feeHistory3.json diff --git a/tests/Ethereum/Data/eth_feeHistory4.json b/tests/chains/Ethereum/Data/eth_feeHistory4.json similarity index 100% rename from tests/Ethereum/Data/eth_feeHistory4.json rename to tests/chains/Ethereum/Data/eth_feeHistory4.json diff --git a/tests/Ethereum/Data/getAmountsOut.json b/tests/chains/Ethereum/Data/getAmountsOut.json similarity index 100% rename from tests/Ethereum/Data/getAmountsOut.json rename to tests/chains/Ethereum/Data/getAmountsOut.json diff --git a/tests/Ethereum/Data/kyber_proxy.json b/tests/chains/Ethereum/Data/kyber_proxy.json similarity index 100% rename from tests/Ethereum/Data/kyber_proxy.json rename to tests/chains/Ethereum/Data/kyber_proxy.json diff --git a/tests/Ethereum/Data/seaport_712.json b/tests/chains/Ethereum/Data/seaport_712.json similarity index 100% rename from tests/Ethereum/Data/seaport_712.json rename to tests/chains/Ethereum/Data/seaport_712.json diff --git a/tests/Ethereum/Data/tuple_nested.json b/tests/chains/Ethereum/Data/tuple_nested.json similarity index 100% rename from tests/Ethereum/Data/tuple_nested.json rename to tests/chains/Ethereum/Data/tuple_nested.json diff --git a/tests/Ethereum/Data/uniswap_router_v2.json b/tests/chains/Ethereum/Data/uniswap_router_v2.json similarity index 100% rename from tests/Ethereum/Data/uniswap_router_v2.json rename to tests/chains/Ethereum/Data/uniswap_router_v2.json diff --git a/tests/Ethereum/Data/zilliqa_data_tx.json b/tests/chains/Ethereum/Data/zilliqa_data_tx.json similarity index 100% rename from tests/Ethereum/Data/zilliqa_data_tx.json rename to tests/chains/Ethereum/Data/zilliqa_data_tx.json diff --git a/tests/Ethereum/RLPTests.cpp b/tests/chains/Ethereum/RLPTests.cpp similarity index 100% rename from tests/Ethereum/RLPTests.cpp rename to tests/chains/Ethereum/RLPTests.cpp diff --git a/tests/Ethereum/SignerTests.cpp b/tests/chains/Ethereum/SignerTests.cpp similarity index 100% rename from tests/Ethereum/SignerTests.cpp rename to tests/chains/Ethereum/SignerTests.cpp diff --git a/tests/Ethereum/TWAnySignerTests.cpp b/tests/chains/Ethereum/TWAnySignerTests.cpp similarity index 99% rename from tests/Ethereum/TWAnySignerTests.cpp rename to tests/chains/Ethereum/TWAnySignerTests.cpp index 727966fa8d8..c50cef17583 100644 --- a/tests/Ethereum/TWAnySignerTests.cpp +++ b/tests/chains/Ethereum/TWAnySignerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include "HexCoding.h" #include "uint256.h" diff --git a/tests/Ethereum/TWCoinTypeTests.cpp b/tests/chains/Ethereum/TWCoinTypeTests.cpp similarity index 97% rename from tests/Ethereum/TWCoinTypeTests.cpp rename to tests/chains/Ethereum/TWCoinTypeTests.cpp index 128fa78357e..39878ade96c 100644 --- a/tests/Ethereum/TWCoinTypeTests.cpp +++ b/tests/chains/Ethereum/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Ethereum/TWEthereumAbiTests.cpp b/tests/chains/Ethereum/TWEthereumAbiTests.cpp similarity index 99% rename from tests/Ethereum/TWEthereumAbiTests.cpp rename to tests/chains/Ethereum/TWEthereumAbiTests.cpp index be8a59d4731..9cb63bd7569 100644 --- a/tests/Ethereum/TWEthereumAbiTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiTests.cpp @@ -13,7 +13,7 @@ #include "HexCoding.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Ethereum/TWEthereumAbiValueDecoderTests.cpp b/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp similarity index 99% rename from tests/Ethereum/TWEthereumAbiValueDecoderTests.cpp rename to tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp index 2f84327195b..da8dcc4b728 100644 --- a/tests/Ethereum/TWEthereumAbiValueDecoderTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp @@ -8,7 +8,7 @@ #include "Data.h" #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Ethereum/TWEthereumAbiValueEncodeTests.cpp b/tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp similarity index 98% rename from tests/Ethereum/TWEthereumAbiValueEncodeTests.cpp rename to tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp index b4b120adbd3..7235666c804 100644 --- a/tests/Ethereum/TWEthereumAbiValueEncodeTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp @@ -9,7 +9,7 @@ #include "Data.h" #include "HexCoding.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Ethereum/ValueDecoderTests.cpp b/tests/chains/Ethereum/ValueDecoderTests.cpp similarity index 100% rename from tests/Ethereum/ValueDecoderTests.cpp rename to tests/chains/Ethereum/ValueDecoderTests.cpp diff --git a/tests/Ethereum/ValueEncoderTests.cpp b/tests/chains/Ethereum/ValueEncoderTests.cpp similarity index 100% rename from tests/Ethereum/ValueEncoderTests.cpp rename to tests/chains/Ethereum/ValueEncoderTests.cpp diff --git a/tests/EthereumClassic/TWCoinTypeTests.cpp b/tests/chains/EthereumClassic/TWCoinTypeTests.cpp similarity index 97% rename from tests/EthereumClassic/TWCoinTypeTests.cpp rename to tests/chains/EthereumClassic/TWCoinTypeTests.cpp index 4e0183374fe..6ac2854fa36 100644 --- a/tests/EthereumClassic/TWCoinTypeTests.cpp +++ b/tests/chains/EthereumClassic/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Everscale/AddressTests.cpp b/tests/chains/Everscale/AddressTests.cpp similarity index 100% rename from tests/Everscale/AddressTests.cpp rename to tests/chains/Everscale/AddressTests.cpp diff --git a/tests/Everscale/CellBuilderTest.cpp b/tests/chains/Everscale/CellBuilderTest.cpp similarity index 100% rename from tests/Everscale/CellBuilderTest.cpp rename to tests/chains/Everscale/CellBuilderTest.cpp diff --git a/tests/Everscale/CellTests.cpp b/tests/chains/Everscale/CellTests.cpp similarity index 100% rename from tests/Everscale/CellTests.cpp rename to tests/chains/Everscale/CellTests.cpp diff --git a/tests/Everscale/SignerTests.cpp b/tests/chains/Everscale/SignerTests.cpp similarity index 100% rename from tests/Everscale/SignerTests.cpp rename to tests/chains/Everscale/SignerTests.cpp diff --git a/tests/Everscale/TWAnyAddressTests.cpp b/tests/chains/Everscale/TWAnyAddressTests.cpp similarity index 96% rename from tests/Everscale/TWAnyAddressTests.cpp rename to tests/chains/Everscale/TWAnyAddressTests.cpp index 74d1212ec58..6cf7627b0f6 100644 --- a/tests/Everscale/TWAnyAddressTests.cpp +++ b/tests/chains/Everscale/TWAnyAddressTests.cpp @@ -7,7 +7,7 @@ #include #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Everscale { diff --git a/tests/Everscale/TWAnySignerTests.cpp b/tests/chains/Everscale/TWAnySignerTests.cpp similarity index 97% rename from tests/Everscale/TWAnySignerTests.cpp rename to tests/chains/Everscale/TWAnySignerTests.cpp index 0dbae0871ee..d404ce00210 100644 --- a/tests/Everscale/TWAnySignerTests.cpp +++ b/tests/chains/Everscale/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include "proto/Everscale.pb.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Everscale { diff --git a/tests/Everscale/TWCoinTypeTests.cpp b/tests/chains/Everscale/TWCoinTypeTests.cpp similarity index 97% rename from tests/Everscale/TWCoinTypeTests.cpp rename to tests/chains/Everscale/TWCoinTypeTests.cpp index b737fe7dc63..95a9297cef2 100644 --- a/tests/Everscale/TWCoinTypeTests.cpp +++ b/tests/chains/Everscale/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Evmos/SignerTests.cpp b/tests/chains/Evmos/SignerTests.cpp similarity index 99% rename from tests/Evmos/SignerTests.cpp rename to tests/chains/Evmos/SignerTests.cpp index 53f599038eb..0dc9d3e816c 100644 --- a/tests/Evmos/SignerTests.cpp +++ b/tests/chains/Evmos/SignerTests.cpp @@ -9,7 +9,7 @@ #include "proto/Cosmos.pb.h" #include "Cosmos/Address.h" #include "Cosmos/Signer.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/Evmos/TWAnyAddressTests.cpp b/tests/chains/Evmos/TWAnyAddressTests.cpp similarity index 97% rename from tests/Evmos/TWAnyAddressTests.cpp rename to tests/chains/Evmos/TWAnyAddressTests.cpp index 5f52cdbf5c4..de3468d4cce 100644 --- a/tests/Evmos/TWAnyAddressTests.cpp +++ b/tests/chains/Evmos/TWAnyAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Evmos/TWCoinTypeTests.cpp b/tests/chains/Evmos/TWCoinTypeTests.cpp similarity index 97% rename from tests/Evmos/TWCoinTypeTests.cpp rename to tests/chains/Evmos/TWCoinTypeTests.cpp index 9e5fdcf643e..346d9c98117 100644 --- a/tests/Evmos/TWCoinTypeTests.cpp +++ b/tests/chains/Evmos/TWCoinTypeTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/FIO/AddressTests.cpp b/tests/chains/FIO/AddressTests.cpp similarity index 100% rename from tests/FIO/AddressTests.cpp rename to tests/chains/FIO/AddressTests.cpp diff --git a/tests/FIO/EncryptionTests.cpp b/tests/chains/FIO/EncryptionTests.cpp similarity index 100% rename from tests/FIO/EncryptionTests.cpp rename to tests/chains/FIO/EncryptionTests.cpp diff --git a/tests/FIO/SignerTests.cpp b/tests/chains/FIO/SignerTests.cpp similarity index 100% rename from tests/FIO/SignerTests.cpp rename to tests/chains/FIO/SignerTests.cpp diff --git a/tests/FIO/TWCoinTypeTests.cpp b/tests/chains/FIO/TWCoinTypeTests.cpp similarity index 97% rename from tests/FIO/TWCoinTypeTests.cpp rename to tests/chains/FIO/TWCoinTypeTests.cpp index fcbb7a47bb6..2dec77e3723 100644 --- a/tests/FIO/TWCoinTypeTests.cpp +++ b/tests/chains/FIO/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/FIO/TWFIOAccountTests.cpp b/tests/chains/FIO/TWFIOAccountTests.cpp similarity index 97% rename from tests/FIO/TWFIOAccountTests.cpp rename to tests/chains/FIO/TWFIOAccountTests.cpp index 3c4764d4801..e9b0f948a09 100644 --- a/tests/FIO/TWFIOAccountTests.cpp +++ b/tests/chains/FIO/TWFIOAccountTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/FIO/TWFIOTests.cpp b/tests/chains/FIO/TWFIOTests.cpp similarity index 99% rename from tests/FIO/TWFIOTests.cpp rename to tests/chains/FIO/TWFIOTests.cpp index 4460eff762a..9777e10f5c5 100644 --- a/tests/FIO/TWFIOTests.cpp +++ b/tests/chains/FIO/TWFIOTests.cpp @@ -10,7 +10,7 @@ #include "proto/FIO.pb.h" #include "FIO/Address.h" #include "Data.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/FIO/TransactionBuilderTests.cpp b/tests/chains/FIO/TransactionBuilderTests.cpp similarity index 100% rename from tests/FIO/TransactionBuilderTests.cpp rename to tests/chains/FIO/TransactionBuilderTests.cpp diff --git a/tests/Fantom/TWCoinTypeTests.cpp b/tests/chains/Fantom/TWCoinTypeTests.cpp similarity index 97% rename from tests/Fantom/TWCoinTypeTests.cpp rename to tests/chains/Fantom/TWCoinTypeTests.cpp index acd79755342..15dcde31182 100644 --- a/tests/Fantom/TWCoinTypeTests.cpp +++ b/tests/chains/Fantom/TWCoinTypeTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Filecoin/AddressTests.cpp b/tests/chains/Filecoin/AddressTests.cpp similarity index 100% rename from tests/Filecoin/AddressTests.cpp rename to tests/chains/Filecoin/AddressTests.cpp diff --git a/tests/Filecoin/SignerTests.cpp b/tests/chains/Filecoin/SignerTests.cpp similarity index 100% rename from tests/Filecoin/SignerTests.cpp rename to tests/chains/Filecoin/SignerTests.cpp diff --git a/tests/Filecoin/TWAnySignerTests.cpp b/tests/chains/Filecoin/TWAnySignerTests.cpp similarity index 98% rename from tests/Filecoin/TWAnySignerTests.cpp rename to tests/chains/Filecoin/TWAnySignerTests.cpp index 0f7c0ae4d5c..a4af47a4332 100644 --- a/tests/Filecoin/TWAnySignerTests.cpp +++ b/tests/chains/Filecoin/TWAnySignerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include "Data.h" diff --git a/tests/Filecoin/TWCoinTypeTests.cpp b/tests/chains/Filecoin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Filecoin/TWCoinTypeTests.cpp rename to tests/chains/Filecoin/TWCoinTypeTests.cpp index 9e5ff95f72e..16d5499b5b1 100644 --- a/tests/Filecoin/TWCoinTypeTests.cpp +++ b/tests/chains/Filecoin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Filecoin/TransactionTests.cpp b/tests/chains/Filecoin/TransactionTests.cpp similarity index 100% rename from tests/Filecoin/TransactionTests.cpp rename to tests/chains/Filecoin/TransactionTests.cpp diff --git a/tests/Firo/TWCoinTypeTests.cpp b/tests/chains/Firo/TWCoinTypeTests.cpp similarity index 97% rename from tests/Firo/TWCoinTypeTests.cpp rename to tests/chains/Firo/TWCoinTypeTests.cpp index b8e7a60f49b..2f22a9c23ac 100644 --- a/tests/Firo/TWCoinTypeTests.cpp +++ b/tests/chains/Firo/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Firo/TWZCoinAddressTests.cpp b/tests/chains/Firo/TWZCoinAddressTests.cpp similarity index 98% rename from tests/Firo/TWZCoinAddressTests.cpp rename to tests/chains/Firo/TWZCoinAddressTests.cpp index 21c495364e5..33ccf2516e8 100644 --- a/tests/Firo/TWZCoinAddressTests.cpp +++ b/tests/chains/Firo/TWZCoinAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/GoChain/TWCoinTypeTests.cpp b/tests/chains/GoChain/TWCoinTypeTests.cpp similarity index 97% rename from tests/GoChain/TWCoinTypeTests.cpp rename to tests/chains/GoChain/TWCoinTypeTests.cpp index 7e8b1b75a94..70e8170b723 100644 --- a/tests/GoChain/TWCoinTypeTests.cpp +++ b/tests/chains/GoChain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Groestlcoin/AddressTests.cpp b/tests/chains/Groestlcoin/AddressTests.cpp similarity index 100% rename from tests/Groestlcoin/AddressTests.cpp rename to tests/chains/Groestlcoin/AddressTests.cpp diff --git a/tests/Groestlcoin/TWCoinTypeTests.cpp b/tests/chains/Groestlcoin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Groestlcoin/TWCoinTypeTests.cpp rename to tests/chains/Groestlcoin/TWCoinTypeTests.cpp index 6187d5d4e85..2cb15c927b2 100644 --- a/tests/Groestlcoin/TWCoinTypeTests.cpp +++ b/tests/chains/Groestlcoin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Groestlcoin/TWGroestlcoinSigningTests.cpp b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp similarity index 99% rename from tests/Groestlcoin/TWGroestlcoinSigningTests.cpp rename to tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp index b2c7127d6e5..994c8977942 100644 --- a/tests/Groestlcoin/TWGroestlcoinSigningTests.cpp +++ b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp @@ -9,7 +9,7 @@ #include "HexCoding.h" #include "PrivateKey.h" #include "proto/Bitcoin.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "../Bitcoin/TxComparisonHelper.h" #include #include diff --git a/tests/Groestlcoin/TWGroestlcoinTests.cpp b/tests/chains/Groestlcoin/TWGroestlcoinTests.cpp similarity index 99% rename from tests/Groestlcoin/TWGroestlcoinTests.cpp rename to tests/chains/Groestlcoin/TWGroestlcoinTests.cpp index a00b684b019..e685bc59646 100644 --- a/tests/Groestlcoin/TWGroestlcoinTests.cpp +++ b/tests/chains/Groestlcoin/TWGroestlcoinTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Harmony/AddressTests.cpp b/tests/chains/Harmony/AddressTests.cpp similarity index 100% rename from tests/Harmony/AddressTests.cpp rename to tests/chains/Harmony/AddressTests.cpp diff --git a/tests/Harmony/SignerTests.cpp b/tests/chains/Harmony/SignerTests.cpp similarity index 100% rename from tests/Harmony/SignerTests.cpp rename to tests/chains/Harmony/SignerTests.cpp diff --git a/tests/Harmony/StakingTests.cpp b/tests/chains/Harmony/StakingTests.cpp similarity index 100% rename from tests/Harmony/StakingTests.cpp rename to tests/chains/Harmony/StakingTests.cpp diff --git a/tests/Harmony/TWAnyAddressTests.cpp b/tests/chains/Harmony/TWAnyAddressTests.cpp similarity index 95% rename from tests/Harmony/TWAnyAddressTests.cpp rename to tests/chains/Harmony/TWAnyAddressTests.cpp index 759eef52c84..a289284b229 100644 --- a/tests/Harmony/TWAnyAddressTests.cpp +++ b/tests/chains/Harmony/TWAnyAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Harmony/TWAnySignerTests.cpp b/tests/chains/Harmony/TWAnySignerTests.cpp similarity index 98% rename from tests/Harmony/TWAnySignerTests.cpp rename to tests/chains/Harmony/TWAnySignerTests.cpp index 5278c3ff18e..3edae0c8247 100644 --- a/tests/Harmony/TWAnySignerTests.cpp +++ b/tests/chains/Harmony/TWAnySignerTests.cpp @@ -8,7 +8,7 @@ #include "HexCoding.h" #include "proto/Harmony.pb.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Harmony/TWCoinTypeTests.cpp b/tests/chains/Harmony/TWCoinTypeTests.cpp similarity index 97% rename from tests/Harmony/TWCoinTypeTests.cpp rename to tests/chains/Harmony/TWCoinTypeTests.cpp index 5541e8ca94f..1147cfeb362 100644 --- a/tests/Harmony/TWCoinTypeTests.cpp +++ b/tests/chains/Harmony/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Harmony/TWHarmonyStakingTests.cpp b/tests/chains/Harmony/TWHarmonyStakingTests.cpp similarity index 99% rename from tests/Harmony/TWHarmonyStakingTests.cpp rename to tests/chains/Harmony/TWHarmonyStakingTests.cpp index 9492225f7a7..164d37edacb 100644 --- a/tests/Harmony/TWHarmonyStakingTests.cpp +++ b/tests/chains/Harmony/TWHarmonyStakingTests.cpp @@ -9,7 +9,7 @@ #include "Harmony/Staking.h" #include "HexCoding.h" #include "PrivateKey.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "proto/Harmony.pb.h" #include "uint256.h" #include diff --git a/tests/Icon/AddressTests.cpp b/tests/chains/ICON/AddressTests.cpp similarity index 100% rename from tests/Icon/AddressTests.cpp rename to tests/chains/ICON/AddressTests.cpp diff --git a/tests/Icon/TWAnySignerTests.cpp b/tests/chains/ICON/TWAnySignerTests.cpp similarity index 97% rename from tests/Icon/TWAnySignerTests.cpp rename to tests/chains/ICON/TWAnySignerTests.cpp index ca9f6b070d3..74a57537568 100644 --- a/tests/Icon/TWAnySignerTests.cpp +++ b/tests/chains/ICON/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include "HexCoding.h" #include "proto/Icon.pb.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/ICON/TWCoinTypeTests.cpp b/tests/chains/ICON/TWCoinTypeTests.cpp similarity index 97% rename from tests/ICON/TWCoinTypeTests.cpp rename to tests/chains/ICON/TWCoinTypeTests.cpp index bffbd0459f0..5fc84a70033 100644 --- a/tests/ICON/TWCoinTypeTests.cpp +++ b/tests/chains/ICON/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/IoTeX/AddressTests.cpp b/tests/chains/IoTeX/AddressTests.cpp similarity index 100% rename from tests/IoTeX/AddressTests.cpp rename to tests/chains/IoTeX/AddressTests.cpp diff --git a/tests/IoTeX/SignerTests.cpp b/tests/chains/IoTeX/SignerTests.cpp similarity index 100% rename from tests/IoTeX/SignerTests.cpp rename to tests/chains/IoTeX/SignerTests.cpp diff --git a/tests/IoTeX/StakingTests.cpp b/tests/chains/IoTeX/StakingTests.cpp similarity index 99% rename from tests/IoTeX/StakingTests.cpp rename to tests/chains/IoTeX/StakingTests.cpp index 8a2008b1908..e5f37330dee 100644 --- a/tests/IoTeX/StakingTests.cpp +++ b/tests/chains/IoTeX/StakingTests.cpp @@ -8,7 +8,7 @@ #include "HexCoding.h" #include "IoTeX/Staking.h" #include "proto/IoTeX.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/IoTeX/TWAnySignerTests.cpp b/tests/chains/IoTeX/TWAnySignerTests.cpp similarity index 97% rename from tests/IoTeX/TWAnySignerTests.cpp rename to tests/chains/IoTeX/TWAnySignerTests.cpp index 9807a5dfb8a..4fbeafd20ac 100644 --- a/tests/IoTeX/TWAnySignerTests.cpp +++ b/tests/chains/IoTeX/TWAnySignerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include "HexCoding.h" #include "proto/IoTeX.pb.h" diff --git a/tests/IoTeX/TWCoinTypeTests.cpp b/tests/chains/IoTeX/TWCoinTypeTests.cpp similarity index 97% rename from tests/IoTeX/TWCoinTypeTests.cpp rename to tests/chains/IoTeX/TWCoinTypeTests.cpp index ce42df73442..4cdc2d68ad4 100644 --- a/tests/IoTeX/TWCoinTypeTests.cpp +++ b/tests/chains/IoTeX/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Kava/TWCoinTypeTests.cpp b/tests/chains/Kava/TWCoinTypeTests.cpp similarity index 97% rename from tests/Kava/TWCoinTypeTests.cpp rename to tests/chains/Kava/TWCoinTypeTests.cpp index 0852f499368..fd4a19b7412 100644 --- a/tests/Kava/TWCoinTypeTests.cpp +++ b/tests/chains/Kava/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/KavaEvm/TWCoinTypeTests.cpp b/tests/chains/KavaEvm/TWCoinTypeTests.cpp similarity index 97% rename from tests/KavaEvm/TWCoinTypeTests.cpp rename to tests/chains/KavaEvm/TWCoinTypeTests.cpp index 70b1d91b97c..a658eba031a 100644 --- a/tests/KavaEvm/TWCoinTypeTests.cpp +++ b/tests/chains/KavaEvm/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Kin/TWCoinTypeTests.cpp b/tests/chains/Kin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Kin/TWCoinTypeTests.cpp rename to tests/chains/Kin/TWCoinTypeTests.cpp index 9120ef0e2f9..8f37b7f3607 100644 --- a/tests/Kin/TWCoinTypeTests.cpp +++ b/tests/chains/Kin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Klaytn/TWCoinTypeTests.cpp b/tests/chains/Klaytn/TWCoinTypeTests.cpp similarity index 97% rename from tests/Klaytn/TWCoinTypeTests.cpp rename to tests/chains/Klaytn/TWCoinTypeTests.cpp index 9f293370262..d7eb5c71136 100644 --- a/tests/Klaytn/TWCoinTypeTests.cpp +++ b/tests/chains/Klaytn/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/KuCoinCommunityChain/TWCoinTypeTests.cpp b/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp similarity index 97% rename from tests/KuCoinCommunityChain/TWCoinTypeTests.cpp rename to tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp index c8069645912..354bc8b7ba1 100644 --- a/tests/KuCoinCommunityChain/TWCoinTypeTests.cpp +++ b/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Kusama/AddressTests.cpp b/tests/chains/Kusama/AddressTests.cpp similarity index 100% rename from tests/Kusama/AddressTests.cpp rename to tests/chains/Kusama/AddressTests.cpp diff --git a/tests/Kusama/SignerTests.cpp b/tests/chains/Kusama/SignerTests.cpp similarity index 100% rename from tests/Kusama/SignerTests.cpp rename to tests/chains/Kusama/SignerTests.cpp diff --git a/tests/Kusama/TWAnySignerTests.cpp b/tests/chains/Kusama/TWAnySignerTests.cpp similarity index 97% rename from tests/Kusama/TWAnySignerTests.cpp rename to tests/chains/Kusama/TWAnySignerTests.cpp index 68875989066..c23abdd9acb 100644 --- a/tests/Kusama/TWAnySignerTests.cpp +++ b/tests/chains/Kusama/TWAnySignerTests.cpp @@ -7,7 +7,7 @@ #include "HexCoding.h" #include "proto/Polkadot.pb.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Kusama/TWCoinTypeTests.cpp b/tests/chains/Kusama/TWCoinTypeTests.cpp similarity index 97% rename from tests/Kusama/TWCoinTypeTests.cpp rename to tests/chains/Kusama/TWCoinTypeTests.cpp index 3ea8f9284f5..09cd2bf8c0f 100644 --- a/tests/Kusama/TWCoinTypeTests.cpp +++ b/tests/chains/Kusama/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Litecoin/LitecoinAddressTests.cpp b/tests/chains/Litecoin/LitecoinAddressTests.cpp similarity index 100% rename from tests/Litecoin/LitecoinAddressTests.cpp rename to tests/chains/Litecoin/LitecoinAddressTests.cpp diff --git a/tests/Litecoin/TWCoinTypeTests.cpp b/tests/chains/Litecoin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Litecoin/TWCoinTypeTests.cpp rename to tests/chains/Litecoin/TWCoinTypeTests.cpp index 3d96044505d..441d729e66d 100644 --- a/tests/Litecoin/TWCoinTypeTests.cpp +++ b/tests/chains/Litecoin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Litecoin/TWLitecoinTests.cpp b/tests/chains/Litecoin/TWLitecoinTests.cpp similarity index 99% rename from tests/Litecoin/TWLitecoinTests.cpp rename to tests/chains/Litecoin/TWLitecoinTests.cpp index 35daa1d0a8c..0008fd375d1 100644 --- a/tests/Litecoin/TWLitecoinTests.cpp +++ b/tests/chains/Litecoin/TWLitecoinTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Meter/TWCoinTypeTests.cpp b/tests/chains/Meter/TWCoinTypeTests.cpp similarity index 97% rename from tests/Meter/TWCoinTypeTests.cpp rename to tests/chains/Meter/TWCoinTypeTests.cpp index 50902798e57..b9c4df9d838 100644 --- a/tests/Meter/TWCoinTypeTests.cpp +++ b/tests/chains/Meter/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Metis/TWCoinTypeTests.cpp b/tests/chains/Metis/TWCoinTypeTests.cpp similarity index 97% rename from tests/Metis/TWCoinTypeTests.cpp rename to tests/chains/Metis/TWCoinTypeTests.cpp index da3e515be54..60733b44741 100644 --- a/tests/Metis/TWCoinTypeTests.cpp +++ b/tests/chains/Metis/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Monacoin/TWCoinTypeTests.cpp b/tests/chains/Monacoin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Monacoin/TWCoinTypeTests.cpp rename to tests/chains/Monacoin/TWCoinTypeTests.cpp index e539e3924e2..82fc75a0c68 100644 --- a/tests/Monacoin/TWCoinTypeTests.cpp +++ b/tests/chains/Monacoin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Monacoin/TWMonacoinAddressTests.cpp b/tests/chains/Monacoin/TWMonacoinAddressTests.cpp similarity index 99% rename from tests/Monacoin/TWMonacoinAddressTests.cpp rename to tests/chains/Monacoin/TWMonacoinAddressTests.cpp index e8f6c7c985d..d46964f25f5 100644 --- a/tests/Monacoin/TWMonacoinAddressTests.cpp +++ b/tests/chains/Monacoin/TWMonacoinAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Monacoin/TWMonacoinTransactionTests.cpp b/tests/chains/Monacoin/TWMonacoinTransactionTests.cpp similarity index 99% rename from tests/Monacoin/TWMonacoinTransactionTests.cpp rename to tests/chains/Monacoin/TWMonacoinTransactionTests.cpp index 29a70f4045f..94f26493815 100644 --- a/tests/Monacoin/TWMonacoinTransactionTests.cpp +++ b/tests/chains/Monacoin/TWMonacoinTransactionTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "PublicKey.h" #include "proto/Bitcoin.pb.h" diff --git a/tests/Moonbeam/TWCoinTypeTests.cpp b/tests/chains/Moonbeam/TWCoinTypeTests.cpp similarity index 97% rename from tests/Moonbeam/TWCoinTypeTests.cpp rename to tests/chains/Moonbeam/TWCoinTypeTests.cpp index 76083ca05e8..0c59369a597 100644 --- a/tests/Moonbeam/TWCoinTypeTests.cpp +++ b/tests/chains/Moonbeam/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Moonriver/TWCoinTypeTests.cpp b/tests/chains/Moonriver/TWCoinTypeTests.cpp similarity index 97% rename from tests/Moonriver/TWCoinTypeTests.cpp rename to tests/chains/Moonriver/TWCoinTypeTests.cpp index 8ef39efaf15..b19f8a84b7b 100644 --- a/tests/Moonriver/TWCoinTypeTests.cpp +++ b/tests/chains/Moonriver/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/NEAR/AccountTests.cpp b/tests/chains/NEAR/AccountTests.cpp similarity index 100% rename from tests/NEAR/AccountTests.cpp rename to tests/chains/NEAR/AccountTests.cpp diff --git a/tests/NEAR/AddressTests.cpp b/tests/chains/NEAR/AddressTests.cpp similarity index 100% rename from tests/NEAR/AddressTests.cpp rename to tests/chains/NEAR/AddressTests.cpp diff --git a/tests/NEAR/SerializationTests.cpp b/tests/chains/NEAR/SerializationTests.cpp similarity index 100% rename from tests/NEAR/SerializationTests.cpp rename to tests/chains/NEAR/SerializationTests.cpp diff --git a/tests/NEAR/SignerTests.cpp b/tests/chains/NEAR/SignerTests.cpp similarity index 100% rename from tests/NEAR/SignerTests.cpp rename to tests/chains/NEAR/SignerTests.cpp diff --git a/tests/NEAR/TWAnySignerTests.cpp b/tests/chains/NEAR/TWAnySignerTests.cpp similarity index 98% rename from tests/NEAR/TWAnySignerTests.cpp rename to tests/chains/NEAR/TWAnySignerTests.cpp index 2f6204022e7..afa81ba9a79 100644 --- a/tests/NEAR/TWAnySignerTests.cpp +++ b/tests/chains/NEAR/TWAnySignerTests.cpp @@ -6,7 +6,7 @@ #include "HexCoding.h" #include "proto/NEAR.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/NEAR/TWCoinTypeTests.cpp b/tests/chains/NEAR/TWCoinTypeTests.cpp similarity index 97% rename from tests/NEAR/TWCoinTypeTests.cpp rename to tests/chains/NEAR/TWCoinTypeTests.cpp index e5b5951a368..c5a04325d1f 100644 --- a/tests/NEAR/TWCoinTypeTests.cpp +++ b/tests/chains/NEAR/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/NEAR/TWNEARAccountTests.cpp b/tests/chains/NEAR/TWNEARAccountTests.cpp similarity index 96% rename from tests/NEAR/TWNEARAccountTests.cpp rename to tests/chains/NEAR/TWNEARAccountTests.cpp index c97ba7d5c0d..0be2cf6dbc7 100644 --- a/tests/NEAR/TWNEARAccountTests.cpp +++ b/tests/chains/NEAR/TWNEARAccountTests.cpp @@ -6,7 +6,7 @@ // // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/NEO/AddressTests.cpp b/tests/chains/NEO/AddressTests.cpp similarity index 100% rename from tests/NEO/AddressTests.cpp rename to tests/chains/NEO/AddressTests.cpp diff --git a/tests/NEO/CoinReferenceTests.cpp b/tests/chains/NEO/CoinReferenceTests.cpp similarity index 100% rename from tests/NEO/CoinReferenceTests.cpp rename to tests/chains/NEO/CoinReferenceTests.cpp diff --git a/tests/NEO/SignerTests.cpp b/tests/chains/NEO/SignerTests.cpp similarity index 100% rename from tests/NEO/SignerTests.cpp rename to tests/chains/NEO/SignerTests.cpp diff --git a/tests/NEO/TWAnySignerTests.cpp b/tests/chains/NEO/TWAnySignerTests.cpp similarity index 99% rename from tests/NEO/TWAnySignerTests.cpp rename to tests/chains/NEO/TWAnySignerTests.cpp index e61fec59506..9a92c1133c0 100644 --- a/tests/NEO/TWAnySignerTests.cpp +++ b/tests/chains/NEO/TWAnySignerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include "HexCoding.h" #include "proto/NEO.pb.h" diff --git a/tests/NEO/TWCoinTypeTests.cpp b/tests/chains/NEO/TWCoinTypeTests.cpp similarity index 97% rename from tests/NEO/TWCoinTypeTests.cpp rename to tests/chains/NEO/TWCoinTypeTests.cpp index 8b986b73a4d..fa87f98119b 100644 --- a/tests/NEO/TWCoinTypeTests.cpp +++ b/tests/chains/NEO/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/NEO/TWNEOAddressTests.cpp b/tests/chains/NEO/TWNEOAddressTests.cpp similarity index 96% rename from tests/NEO/TWNEOAddressTests.cpp rename to tests/chains/NEO/TWNEOAddressTests.cpp index 029f3bdb95b..cc36db2af5f 100644 --- a/tests/NEO/TWNEOAddressTests.cpp +++ b/tests/chains/NEO/TWNEOAddressTests.cpp @@ -9,7 +9,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/NEO/TransactionAttributeTests.cpp b/tests/chains/NEO/TransactionAttributeTests.cpp similarity index 100% rename from tests/NEO/TransactionAttributeTests.cpp rename to tests/chains/NEO/TransactionAttributeTests.cpp diff --git a/tests/NEO/TransactionOutputTests.cpp b/tests/chains/NEO/TransactionOutputTests.cpp similarity index 100% rename from tests/NEO/TransactionOutputTests.cpp rename to tests/chains/NEO/TransactionOutputTests.cpp diff --git a/tests/NEO/TransactionTests.cpp b/tests/chains/NEO/TransactionTests.cpp similarity index 100% rename from tests/NEO/TransactionTests.cpp rename to tests/chains/NEO/TransactionTests.cpp diff --git a/tests/NEO/WitnessTests.cpp b/tests/chains/NEO/WitnessTests.cpp similarity index 100% rename from tests/NEO/WitnessTests.cpp rename to tests/chains/NEO/WitnessTests.cpp diff --git a/tests/NULS/AddressTests.cpp b/tests/chains/NULS/AddressTests.cpp similarity index 100% rename from tests/NULS/AddressTests.cpp rename to tests/chains/NULS/AddressTests.cpp diff --git a/tests/NULS/TWAnySignerTests.cpp b/tests/chains/NULS/TWAnySignerTests.cpp similarity index 97% rename from tests/NULS/TWAnySignerTests.cpp rename to tests/chains/NULS/TWAnySignerTests.cpp index c0f5c3b0eea..749de87342b 100644 --- a/tests/NULS/TWAnySignerTests.cpp +++ b/tests/chains/NULS/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::NULS::tests { diff --git a/tests/NULS/TWCoinTypeTests.cpp b/tests/chains/NULS/TWCoinTypeTests.cpp similarity index 97% rename from tests/NULS/TWCoinTypeTests.cpp rename to tests/chains/NULS/TWCoinTypeTests.cpp index 3d048c09faf..5618222dcb2 100644 --- a/tests/NULS/TWCoinTypeTests.cpp +++ b/tests/chains/NULS/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nano/AddressTests.cpp b/tests/chains/Nano/AddressTests.cpp similarity index 100% rename from tests/Nano/AddressTests.cpp rename to tests/chains/Nano/AddressTests.cpp diff --git a/tests/Nano/SignerTests.cpp b/tests/chains/Nano/SignerTests.cpp similarity index 100% rename from tests/Nano/SignerTests.cpp rename to tests/chains/Nano/SignerTests.cpp diff --git a/tests/Nano/TWAnySignerTests.cpp b/tests/chains/Nano/TWAnySignerTests.cpp similarity index 98% rename from tests/Nano/TWAnySignerTests.cpp rename to tests/chains/Nano/TWAnySignerTests.cpp index a348de09dae..aa76abc83f5 100644 --- a/tests/Nano/TWAnySignerTests.cpp +++ b/tests/chains/Nano/TWAnySignerTests.cpp @@ -7,7 +7,7 @@ #include "HexCoding.h" #include "proto/Nano.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nano/TWCoinTypeTests.cpp b/tests/chains/Nano/TWCoinTypeTests.cpp similarity index 97% rename from tests/Nano/TWCoinTypeTests.cpp rename to tests/chains/Nano/TWCoinTypeTests.cpp index 37be0b6aa62..c6458843538 100644 --- a/tests/Nano/TWCoinTypeTests.cpp +++ b/tests/chains/Nano/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nano/TWNanoAddressTests.cpp b/tests/chains/Nano/TWNanoAddressTests.cpp similarity index 97% rename from tests/Nano/TWNanoAddressTests.cpp rename to tests/chains/Nano/TWNanoAddressTests.cpp index afcd1587cf4..44d653119aa 100644 --- a/tests/Nano/TWNanoAddressTests.cpp +++ b/tests/chains/Nano/TWNanoAddressTests.cpp @@ -5,7 +5,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" diff --git a/tests/NativeEvmos/TWAnyAddressTests.cpp b/tests/chains/NativeEvmos/TWAnyAddressTests.cpp similarity index 97% rename from tests/NativeEvmos/TWAnyAddressTests.cpp rename to tests/chains/NativeEvmos/TWAnyAddressTests.cpp index 410da049ba9..ed2a02cd50f 100644 --- a/tests/NativeEvmos/TWAnyAddressTests.cpp +++ b/tests/chains/NativeEvmos/TWAnyAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/NativeEvmos/TWCoinTypeTests.cpp b/tests/chains/NativeEvmos/TWCoinTypeTests.cpp similarity index 97% rename from tests/NativeEvmos/TWCoinTypeTests.cpp rename to tests/chains/NativeEvmos/TWCoinTypeTests.cpp index fdcc17272f5..6df42eaa394 100644 --- a/tests/NativeEvmos/TWCoinTypeTests.cpp +++ b/tests/chains/NativeEvmos/TWCoinTypeTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nebulas/AddressTests.cpp b/tests/chains/Nebulas/AddressTests.cpp similarity index 100% rename from tests/Nebulas/AddressTests.cpp rename to tests/chains/Nebulas/AddressTests.cpp diff --git a/tests/Nebulas/SignerTests.cpp b/tests/chains/Nebulas/SignerTests.cpp similarity index 100% rename from tests/Nebulas/SignerTests.cpp rename to tests/chains/Nebulas/SignerTests.cpp diff --git a/tests/Nebulas/TWAnySignerTests.cpp b/tests/chains/Nebulas/TWAnySignerTests.cpp similarity index 97% rename from tests/Nebulas/TWAnySignerTests.cpp rename to tests/chains/Nebulas/TWAnySignerTests.cpp index bafee5a3fcf..c850acf6935 100644 --- a/tests/Nebulas/TWAnySignerTests.cpp +++ b/tests/chains/Nebulas/TWAnySignerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include "HexCoding.h" #include "uint256.h" diff --git a/tests/Nebulas/TWCoinTypeTests.cpp b/tests/chains/Nebulas/TWCoinTypeTests.cpp similarity index 97% rename from tests/Nebulas/TWCoinTypeTests.cpp rename to tests/chains/Nebulas/TWCoinTypeTests.cpp index c3eac69cffa..d0f30bcc619 100644 --- a/tests/Nebulas/TWCoinTypeTests.cpp +++ b/tests/chains/Nebulas/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nebulas/TWNebulasAddressTests.cpp b/tests/chains/Nebulas/TWNebulasAddressTests.cpp similarity index 98% rename from tests/Nebulas/TWNebulasAddressTests.cpp rename to tests/chains/Nebulas/TWNebulasAddressTests.cpp index 32e7ae08fa4..50a2cdcea9b 100644 --- a/tests/Nebulas/TWNebulasAddressTests.cpp +++ b/tests/chains/Nebulas/TWNebulasAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nebulas/TransactionTests.cpp b/tests/chains/Nebulas/TransactionTests.cpp similarity index 100% rename from tests/Nebulas/TransactionTests.cpp rename to tests/chains/Nebulas/TransactionTests.cpp diff --git a/tests/Nervos/AddressTests.cpp b/tests/chains/Nervos/AddressTests.cpp similarity index 100% rename from tests/Nervos/AddressTests.cpp rename to tests/chains/Nervos/AddressTests.cpp diff --git a/tests/Nervos/SignerTests.cpp b/tests/chains/Nervos/SignerTests.cpp similarity index 99% rename from tests/Nervos/SignerTests.cpp rename to tests/chains/Nervos/SignerTests.cpp index b7ea9e3645f..1b8bdfefeb6 100644 --- a/tests/Nervos/SignerTests.cpp +++ b/tests/chains/Nervos/SignerTests.cpp @@ -14,7 +14,7 @@ #include "PrivateKey.h" #include "PublicKey.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nervos/TWAnyAddressTests.cpp b/tests/chains/Nervos/TWAnyAddressTests.cpp similarity index 98% rename from tests/Nervos/TWAnyAddressTests.cpp rename to tests/chains/Nervos/TWAnyAddressTests.cpp index c948853f3ec..c514723797c 100644 --- a/tests/Nervos/TWAnyAddressTests.cpp +++ b/tests/chains/Nervos/TWAnyAddressTests.cpp @@ -12,7 +12,7 @@ #include #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Nervos/TWAnySignerTests.cpp b/tests/chains/Nervos/TWAnySignerTests.cpp similarity index 99% rename from tests/Nervos/TWAnySignerTests.cpp rename to tests/chains/Nervos/TWAnySignerTests.cpp index 79a4b91079c..c213e802d0a 100644 --- a/tests/Nervos/TWAnySignerTests.cpp +++ b/tests/chains/Nervos/TWAnySignerTests.cpp @@ -11,7 +11,7 @@ #include "PublicKey.h" #include "proto/Nervos.pb.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nervos/TWCoinTypeTests.cpp b/tests/chains/Nervos/TWCoinTypeTests.cpp similarity index 97% rename from tests/Nervos/TWCoinTypeTests.cpp rename to tests/chains/Nervos/TWCoinTypeTests.cpp index bff876ae0c3..1150536151a 100644 --- a/tests/Nervos/TWCoinTypeTests.cpp +++ b/tests/chains/Nervos/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nervos/TWNervosAddressTests.cpp b/tests/chains/Nervos/TWNervosAddressTests.cpp similarity index 96% rename from tests/Nervos/TWNervosAddressTests.cpp rename to tests/chains/Nervos/TWNervosAddressTests.cpp index 1fa5924000c..cf7a8560e0d 100644 --- a/tests/Nervos/TWNervosAddressTests.cpp +++ b/tests/chains/Nervos/TWNervosAddressTests.cpp @@ -8,7 +8,7 @@ #include "PrivateKey.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Nervos::tests { diff --git a/tests/Nimiq/AddressTests.cpp b/tests/chains/Nimiq/AddressTests.cpp similarity index 100% rename from tests/Nimiq/AddressTests.cpp rename to tests/chains/Nimiq/AddressTests.cpp diff --git a/tests/Nimiq/SignerTests.cpp b/tests/chains/Nimiq/SignerTests.cpp similarity index 100% rename from tests/Nimiq/SignerTests.cpp rename to tests/chains/Nimiq/SignerTests.cpp diff --git a/tests/Nimiq/TWAnySignerTests.cpp b/tests/chains/Nimiq/TWAnySignerTests.cpp similarity index 96% rename from tests/Nimiq/TWAnySignerTests.cpp rename to tests/chains/Nimiq/TWAnySignerTests.cpp index bc7bcaf9fec..b9a9ee32b24 100644 --- a/tests/Nimiq/TWAnySignerTests.cpp +++ b/tests/chains/Nimiq/TWAnySignerTests.cpp @@ -8,7 +8,7 @@ #include "proto/Nimiq.pb.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Nimiq::tests { diff --git a/tests/Nimiq/TWCoinTypeTests.cpp b/tests/chains/Nimiq/TWCoinTypeTests.cpp similarity index 97% rename from tests/Nimiq/TWCoinTypeTests.cpp rename to tests/chains/Nimiq/TWCoinTypeTests.cpp index 40f585625e3..f973c71604e 100644 --- a/tests/Nimiq/TWCoinTypeTests.cpp +++ b/tests/chains/Nimiq/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Nimiq/TransactionTests.cpp b/tests/chains/Nimiq/TransactionTests.cpp similarity index 100% rename from tests/Nimiq/TransactionTests.cpp rename to tests/chains/Nimiq/TransactionTests.cpp diff --git a/tests/OKXChain/TWCoinTypeTests.cpp b/tests/chains/OKXChain/TWCoinTypeTests.cpp similarity index 97% rename from tests/OKXChain/TWCoinTypeTests.cpp rename to tests/chains/OKXChain/TWCoinTypeTests.cpp index 4e68b569a69..af4eed1e128 100644 --- a/tests/OKXChain/TWCoinTypeTests.cpp +++ b/tests/chains/OKXChain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Oasis/AddressTests.cpp b/tests/chains/Oasis/AddressTests.cpp similarity index 100% rename from tests/Oasis/AddressTests.cpp rename to tests/chains/Oasis/AddressTests.cpp diff --git a/tests/Oasis/SignerTests.cpp b/tests/chains/Oasis/SignerTests.cpp similarity index 100% rename from tests/Oasis/SignerTests.cpp rename to tests/chains/Oasis/SignerTests.cpp diff --git a/tests/Oasis/TWAnySignerTests.cpp b/tests/chains/Oasis/TWAnySignerTests.cpp similarity index 97% rename from tests/Oasis/TWAnySignerTests.cpp rename to tests/chains/Oasis/TWAnySignerTests.cpp index 5e1c2b504ee..d390b051db5 100644 --- a/tests/Oasis/TWAnySignerTests.cpp +++ b/tests/chains/Oasis/TWAnySignerTests.cpp @@ -8,7 +8,7 @@ #include "HexCoding.h" #include "proto/Oasis.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Oasis/TWCoinTypeTests.cpp b/tests/chains/Oasis/TWCoinTypeTests.cpp similarity index 97% rename from tests/Oasis/TWCoinTypeTests.cpp rename to tests/chains/Oasis/TWCoinTypeTests.cpp index a75e79d0796..86ff3764832 100644 --- a/tests/Oasis/TWCoinTypeTests.cpp +++ b/tests/chains/Oasis/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Ontology/AccountTests.cpp b/tests/chains/Ontology/AccountTests.cpp similarity index 100% rename from tests/Ontology/AccountTests.cpp rename to tests/chains/Ontology/AccountTests.cpp diff --git a/tests/Ontology/AddressTests.cpp b/tests/chains/Ontology/AddressTests.cpp similarity index 100% rename from tests/Ontology/AddressTests.cpp rename to tests/chains/Ontology/AddressTests.cpp diff --git a/tests/Ontology/Oep4Tests.cpp b/tests/chains/Ontology/Oep4Tests.cpp similarity index 100% rename from tests/Ontology/Oep4Tests.cpp rename to tests/chains/Ontology/Oep4Tests.cpp diff --git a/tests/Ontology/OngTests.cpp b/tests/chains/Ontology/OngTests.cpp similarity index 100% rename from tests/Ontology/OngTests.cpp rename to tests/chains/Ontology/OngTests.cpp diff --git a/tests/Ontology/OntTests.cpp b/tests/chains/Ontology/OntTests.cpp similarity index 100% rename from tests/Ontology/OntTests.cpp rename to tests/chains/Ontology/OntTests.cpp diff --git a/tests/Ontology/ParamsBuilderTests.cpp b/tests/chains/Ontology/ParamsBuilderTests.cpp similarity index 100% rename from tests/Ontology/ParamsBuilderTests.cpp rename to tests/chains/Ontology/ParamsBuilderTests.cpp diff --git a/tests/Ontology/TWAnySignerTests.cpp b/tests/chains/Ontology/TWAnySignerTests.cpp similarity index 99% rename from tests/Ontology/TWAnySignerTests.cpp rename to tests/chains/Ontology/TWAnySignerTests.cpp index e25b1e46190..99f2c8b1559 100644 --- a/tests/Ontology/TWAnySignerTests.cpp +++ b/tests/chains/Ontology/TWAnySignerTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Ontology/Oep4TxBuilder.h" #include "Ontology/OngTxBuilder.h" diff --git a/tests/Ontology/TWCoinTypeTests.cpp b/tests/chains/Ontology/TWCoinTypeTests.cpp similarity index 97% rename from tests/Ontology/TWCoinTypeTests.cpp rename to tests/chains/Ontology/TWCoinTypeTests.cpp index 733c1fe94cf..4bada2c0a47 100644 --- a/tests/Ontology/TWCoinTypeTests.cpp +++ b/tests/chains/Ontology/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Ontology/TransactionTests.cpp b/tests/chains/Ontology/TransactionTests.cpp similarity index 100% rename from tests/Ontology/TransactionTests.cpp rename to tests/chains/Ontology/TransactionTests.cpp diff --git a/tests/Optimism/TWCoinTypeTests.cpp b/tests/chains/Optimism/TWCoinTypeTests.cpp similarity index 97% rename from tests/Optimism/TWCoinTypeTests.cpp rename to tests/chains/Optimism/TWCoinTypeTests.cpp index 4073c8896b8..1cc6ff0e4c3 100644 --- a/tests/Optimism/TWCoinTypeTests.cpp +++ b/tests/chains/Optimism/TWCoinTypeTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Osmosis/AddressTests.cpp b/tests/chains/Osmosis/AddressTests.cpp similarity index 100% rename from tests/Osmosis/AddressTests.cpp rename to tests/chains/Osmosis/AddressTests.cpp diff --git a/tests/Osmosis/SignerTests.cpp b/tests/chains/Osmosis/SignerTests.cpp similarity index 98% rename from tests/Osmosis/SignerTests.cpp rename to tests/chains/Osmosis/SignerTests.cpp index a418f5fc6fb..91dd335f25e 100644 --- a/tests/Osmosis/SignerTests.cpp +++ b/tests/chains/Osmosis/SignerTests.cpp @@ -9,7 +9,7 @@ #include "HexCoding.h" #include "PublicKey.h" #include "proto/Cosmos.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/Osmosis/TWAnyAddressTests.cpp b/tests/chains/Osmosis/TWAnyAddressTests.cpp similarity index 96% rename from tests/Osmosis/TWAnyAddressTests.cpp rename to tests/chains/Osmosis/TWAnyAddressTests.cpp index 58e8d8abb24..40793a73acf 100644 --- a/tests/Osmosis/TWAnyAddressTests.cpp +++ b/tests/chains/Osmosis/TWAnyAddressTests.cpp @@ -7,7 +7,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Osmosis/TWAnySignerTests.cpp b/tests/chains/Osmosis/TWAnySignerTests.cpp similarity index 98% rename from tests/Osmosis/TWAnySignerTests.cpp rename to tests/chains/Osmosis/TWAnySignerTests.cpp index 5985d55949c..95f230551a8 100644 --- a/tests/Osmosis/TWAnySignerTests.cpp +++ b/tests/chains/Osmosis/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include "proto/Cosmos.pb.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Cosmos::tests { diff --git a/tests/Osmosis/TWCoinTypeTests.cpp b/tests/chains/Osmosis/TWCoinTypeTests.cpp similarity index 97% rename from tests/Osmosis/TWCoinTypeTests.cpp rename to tests/chains/Osmosis/TWCoinTypeTests.cpp index dc9b81fe90e..d42a4106616 100644 --- a/tests/Osmosis/TWCoinTypeTests.cpp +++ b/tests/chains/Osmosis/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/POANetwork/TWCoinTypeTests.cpp b/tests/chains/POANetwork/TWCoinTypeTests.cpp similarity index 97% rename from tests/POANetwork/TWCoinTypeTests.cpp rename to tests/chains/POANetwork/TWCoinTypeTests.cpp index 37ca284bc69..76581a52843 100644 --- a/tests/POANetwork/TWCoinTypeTests.cpp +++ b/tests/chains/POANetwork/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Polkadot/AddressTests.cpp b/tests/chains/Polkadot/AddressTests.cpp similarity index 100% rename from tests/Polkadot/AddressTests.cpp rename to tests/chains/Polkadot/AddressTests.cpp diff --git a/tests/Polkadot/SS58AddressTests.cpp b/tests/chains/Polkadot/SS58AddressTests.cpp similarity index 99% rename from tests/Polkadot/SS58AddressTests.cpp rename to tests/chains/Polkadot/SS58AddressTests.cpp index b8db84c66af..701938d697b 100644 --- a/tests/Polkadot/SS58AddressTests.cpp +++ b/tests/chains/Polkadot/SS58AddressTests.cpp @@ -7,7 +7,7 @@ #include "Polkadot/SS58Address.h" #include "HexCoding.h" #include "PublicKey.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Polkadot/ScaleCodecTests.cpp b/tests/chains/Polkadot/ScaleCodecTests.cpp similarity index 100% rename from tests/Polkadot/ScaleCodecTests.cpp rename to tests/chains/Polkadot/ScaleCodecTests.cpp diff --git a/tests/Polkadot/SignerTests.cpp b/tests/chains/Polkadot/SignerTests.cpp similarity index 100% rename from tests/Polkadot/SignerTests.cpp rename to tests/chains/Polkadot/SignerTests.cpp diff --git a/tests/Polkadot/TWCoinTypeTests.cpp b/tests/chains/Polkadot/TWCoinTypeTests.cpp similarity index 97% rename from tests/Polkadot/TWCoinTypeTests.cpp rename to tests/chains/Polkadot/TWCoinTypeTests.cpp index f5504a5c30f..b35b069a115 100644 --- a/tests/Polkadot/TWCoinTypeTests.cpp +++ b/tests/chains/Polkadot/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Polygon/TWCoinTypeTests.cpp b/tests/chains/Polygon/TWCoinTypeTests.cpp similarity index 97% rename from tests/Polygon/TWCoinTypeTests.cpp rename to tests/chains/Polygon/TWCoinTypeTests.cpp index a1810ade109..3ffc1203d40 100644 --- a/tests/Polygon/TWCoinTypeTests.cpp +++ b/tests/chains/Polygon/TWCoinTypeTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Qtum/TWCoinTypeTests.cpp b/tests/chains/Qtum/TWCoinTypeTests.cpp similarity index 97% rename from tests/Qtum/TWCoinTypeTests.cpp rename to tests/chains/Qtum/TWCoinTypeTests.cpp index bef8e814b59..97814e04ab3 100644 --- a/tests/Qtum/TWCoinTypeTests.cpp +++ b/tests/chains/Qtum/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Qtum/TWQtumAddressTests.cpp b/tests/chains/Qtum/TWQtumAddressTests.cpp similarity index 99% rename from tests/Qtum/TWQtumAddressTests.cpp rename to tests/chains/Qtum/TWQtumAddressTests.cpp index 8d658cf096c..7da7788ce7e 100644 --- a/tests/Qtum/TWQtumAddressTests.cpp +++ b/tests/chains/Qtum/TWQtumAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Ravencoin/TWCoinTypeTests.cpp b/tests/chains/Ravencoin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Ravencoin/TWCoinTypeTests.cpp rename to tests/chains/Ravencoin/TWCoinTypeTests.cpp index f639dfe6bba..9f90f507883 100644 --- a/tests/Ravencoin/TWCoinTypeTests.cpp +++ b/tests/chains/Ravencoin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Ravencoin/TWRavencoinTransactionTests.cpp b/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp similarity index 99% rename from tests/Ravencoin/TWRavencoinTransactionTests.cpp rename to tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp index b90d76b91c8..d3927772a2c 100644 --- a/tests/Ravencoin/TWRavencoinTransactionTests.cpp +++ b/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/TransactionBuilder.h" diff --git a/tests/Ronin/TWAnyAddressTests.cpp b/tests/chains/Ronin/TWAnyAddressTests.cpp similarity index 98% rename from tests/Ronin/TWAnyAddressTests.cpp rename to tests/chains/Ronin/TWAnyAddressTests.cpp index d2e0bde63c7..8bdcd75d68f 100644 --- a/tests/Ronin/TWAnyAddressTests.cpp +++ b/tests/chains/Ronin/TWAnyAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Ronin/TWAnySignerTests.cpp b/tests/chains/Ronin/TWAnySignerTests.cpp similarity index 97% rename from tests/Ronin/TWAnySignerTests.cpp rename to tests/chains/Ronin/TWAnySignerTests.cpp index 3f852a1dab2..cabaf560b68 100644 --- a/tests/Ronin/TWAnySignerTests.cpp +++ b/tests/chains/Ronin/TWAnySignerTests.cpp @@ -7,7 +7,7 @@ #include "HexCoding.h" #include "proto/Ethereum.pb.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Ronin/TWCoinTypeTests.cpp b/tests/chains/Ronin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Ronin/TWCoinTypeTests.cpp rename to tests/chains/Ronin/TWCoinTypeTests.cpp index 5d29a67771e..3549ef495a0 100644 --- a/tests/Ronin/TWCoinTypeTests.cpp +++ b/tests/chains/Ronin/TWCoinTypeTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/SmartBitcoinCash/TWCoinTypeTests.cpp b/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp similarity index 97% rename from tests/SmartBitcoinCash/TWCoinTypeTests.cpp rename to tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp index 04b97444049..f08f7466371 100644 --- a/tests/SmartBitcoinCash/TWCoinTypeTests.cpp +++ b/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Solana/AddressTests.cpp b/tests/chains/Solana/AddressTests.cpp similarity index 100% rename from tests/Solana/AddressTests.cpp rename to tests/chains/Solana/AddressTests.cpp diff --git a/tests/Solana/ProgramTests.cpp b/tests/chains/Solana/ProgramTests.cpp similarity index 100% rename from tests/Solana/ProgramTests.cpp rename to tests/chains/Solana/ProgramTests.cpp diff --git a/tests/Solana/SignerTests.cpp b/tests/chains/Solana/SignerTests.cpp similarity index 100% rename from tests/Solana/SignerTests.cpp rename to tests/chains/Solana/SignerTests.cpp diff --git a/tests/Solana/TWAnySignerTests.cpp b/tests/chains/Solana/TWAnySignerTests.cpp similarity index 99% rename from tests/Solana/TWAnySignerTests.cpp rename to tests/chains/Solana/TWAnySignerTests.cpp index 2a0dd1ca007..8246f8f48ce 100644 --- a/tests/Solana/TWAnySignerTests.cpp +++ b/tests/chains/Solana/TWAnySignerTests.cpp @@ -10,7 +10,7 @@ #include "Solana/Address.h" #include "Solana/Program.h" #include "PrivateKey.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Solana/TWCoinTypeTests.cpp b/tests/chains/Solana/TWCoinTypeTests.cpp similarity index 97% rename from tests/Solana/TWCoinTypeTests.cpp rename to tests/chains/Solana/TWCoinTypeTests.cpp index 5bd9998488a..c6225566d1e 100644 --- a/tests/Solana/TWCoinTypeTests.cpp +++ b/tests/chains/Solana/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Solana/TWSolanaAddressTests.cpp b/tests/chains/Solana/TWSolanaAddressTests.cpp similarity index 97% rename from tests/Solana/TWSolanaAddressTests.cpp rename to tests/chains/Solana/TWSolanaAddressTests.cpp index 2ad4f854fcd..1cdd42676f2 100644 --- a/tests/Solana/TWSolanaAddressTests.cpp +++ b/tests/chains/Solana/TWSolanaAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Solana/TransactionTests.cpp b/tests/chains/Solana/TransactionTests.cpp similarity index 100% rename from tests/Solana/TransactionTests.cpp rename to tests/chains/Solana/TransactionTests.cpp diff --git a/tests/Stellar/AddressTests.cpp b/tests/chains/Stellar/AddressTests.cpp similarity index 100% rename from tests/Stellar/AddressTests.cpp rename to tests/chains/Stellar/AddressTests.cpp diff --git a/tests/Stellar/TWAnySignerTests.cpp b/tests/chains/Stellar/TWAnySignerTests.cpp similarity index 99% rename from tests/Stellar/TWAnySignerTests.cpp rename to tests/chains/Stellar/TWAnySignerTests.cpp index 739f0926bca..7ad662a073a 100644 --- a/tests/Stellar/TWAnySignerTests.cpp +++ b/tests/chains/Stellar/TWAnySignerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/Stellar/TWCoinTypeTests.cpp b/tests/chains/Stellar/TWCoinTypeTests.cpp similarity index 97% rename from tests/Stellar/TWCoinTypeTests.cpp rename to tests/chains/Stellar/TWCoinTypeTests.cpp index 12164f9b2d6..2fc1c59fb4b 100644 --- a/tests/Stellar/TWCoinTypeTests.cpp +++ b/tests/chains/Stellar/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Stellar/TWStellarAddressTests.cpp b/tests/chains/Stellar/TWStellarAddressTests.cpp similarity index 96% rename from tests/Stellar/TWStellarAddressTests.cpp rename to tests/chains/Stellar/TWStellarAddressTests.cpp index 47d1f759c76..97f9fc32f12 100644 --- a/tests/Stellar/TWStellarAddressTests.cpp +++ b/tests/chains/Stellar/TWStellarAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Stellar/TransactionTests.cpp b/tests/chains/Stellar/TransactionTests.cpp similarity index 99% rename from tests/Stellar/TransactionTests.cpp rename to tests/chains/Stellar/TransactionTests.cpp index b80e60a0274..8a7890eac2e 100644 --- a/tests/Stellar/TransactionTests.cpp +++ b/tests/chains/Stellar/TransactionTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Stellar/Signer.h" #include "HexCoding.h" diff --git a/tests/THORChain/SignerTests.cpp b/tests/chains/THORChain/SignerTests.cpp similarity index 99% rename from tests/THORChain/SignerTests.cpp rename to tests/chains/THORChain/SignerTests.cpp index c283cae8bf2..85b6bbf68fc 100644 --- a/tests/THORChain/SignerTests.cpp +++ b/tests/chains/THORChain/SignerTests.cpp @@ -8,7 +8,7 @@ #include "THORChain/Signer.h" #include "HexCoding.h" #include "Bech32Address.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp similarity index 99% rename from tests/THORChain/SwapTests.cpp rename to tests/chains/THORChain/SwapTests.cpp index adb3340a417..1f0738595e4 100644 --- a/tests/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -22,7 +22,7 @@ #include #include #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/THORChain/TWAnyAddressTests.cpp b/tests/chains/THORChain/TWAnyAddressTests.cpp similarity index 96% rename from tests/THORChain/TWAnyAddressTests.cpp rename to tests/chains/THORChain/TWAnyAddressTests.cpp index 28e1618885e..52d9bc560cb 100644 --- a/tests/THORChain/TWAnyAddressTests.cpp +++ b/tests/chains/THORChain/TWAnyAddressTests.cpp @@ -7,7 +7,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/THORChain/TWAnySignerTests.cpp b/tests/chains/THORChain/TWAnySignerTests.cpp similarity index 98% rename from tests/THORChain/TWAnySignerTests.cpp rename to tests/chains/THORChain/TWAnySignerTests.cpp index a4fb0eebde3..8e9e45380d7 100644 --- a/tests/THORChain/TWAnySignerTests.cpp +++ b/tests/chains/THORChain/TWAnySignerTests.cpp @@ -10,7 +10,7 @@ #include "proto/Cosmos.pb.h" #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/THORChain/TWCoinTypeTests.cpp b/tests/chains/THORChain/TWCoinTypeTests.cpp similarity index 97% rename from tests/THORChain/TWCoinTypeTests.cpp rename to tests/chains/THORChain/TWCoinTypeTests.cpp index fead42b2449..de7f561af47 100644 --- a/tests/THORChain/TWCoinTypeTests.cpp +++ b/tests/chains/THORChain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/THORChain/TWSwapTests.cpp b/tests/chains/THORChain/TWSwapTests.cpp similarity index 99% rename from tests/THORChain/TWSwapTests.cpp rename to tests/chains/THORChain/TWSwapTests.cpp index d2af7e2a18e..ea266cb6584 100644 --- a/tests/THORChain/TWSwapTests.cpp +++ b/tests/chains/THORChain/TWSwapTests.cpp @@ -16,7 +16,7 @@ #include "HexCoding.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/Terra/SignerTests.cpp b/tests/chains/Terra/SignerTests.cpp similarity index 99% rename from tests/Terra/SignerTests.cpp rename to tests/chains/Terra/SignerTests.cpp index ee67a38dddc..34c01feb560 100644 --- a/tests/Terra/SignerTests.cpp +++ b/tests/chains/Terra/SignerTests.cpp @@ -11,7 +11,7 @@ #include "Cosmos/Signer.h" #include "Cosmos/ProtobufSerialization.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Terra/TWCoinTypeTests.cpp b/tests/chains/Terra/TWCoinTypeTests.cpp similarity index 97% rename from tests/Terra/TWCoinTypeTests.cpp rename to tests/chains/Terra/TWCoinTypeTests.cpp index e46e7c58f0c..464be13fa31 100644 --- a/tests/Terra/TWCoinTypeTests.cpp +++ b/tests/chains/Terra/TWCoinTypeTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/TerraV2/SignerTests.cpp b/tests/chains/TerraV2/SignerTests.cpp similarity index 99% rename from tests/TerraV2/SignerTests.cpp rename to tests/chains/TerraV2/SignerTests.cpp index 4978a5c4405..b1cc0c152b2 100644 --- a/tests/TerraV2/SignerTests.cpp +++ b/tests/chains/TerraV2/SignerTests.cpp @@ -11,7 +11,7 @@ #include "Cosmos/Signer.h" #include "Cosmos/ProtobufSerialization.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/TerraV2/TWCoinTypeTests.cpp b/tests/chains/TerraV2/TWCoinTypeTests.cpp similarity index 97% rename from tests/TerraV2/TWCoinTypeTests.cpp rename to tests/chains/TerraV2/TWCoinTypeTests.cpp index 96c27cff876..ee436963959 100644 --- a/tests/TerraV2/TWCoinTypeTests.cpp +++ b/tests/chains/TerraV2/TWCoinTypeTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Tezos/AddressTests.cpp b/tests/chains/Tezos/AddressTests.cpp similarity index 100% rename from tests/Tezos/AddressTests.cpp rename to tests/chains/Tezos/AddressTests.cpp diff --git a/tests/Tezos/ForgingTests.cpp b/tests/chains/Tezos/ForgingTests.cpp similarity index 100% rename from tests/Tezos/ForgingTests.cpp rename to tests/chains/Tezos/ForgingTests.cpp diff --git a/tests/Tezos/OperationListTests.cpp b/tests/chains/Tezos/OperationListTests.cpp similarity index 100% rename from tests/Tezos/OperationListTests.cpp rename to tests/chains/Tezos/OperationListTests.cpp diff --git a/tests/Tezos/PublicKeyTests.cpp b/tests/chains/Tezos/PublicKeyTests.cpp similarity index 100% rename from tests/Tezos/PublicKeyTests.cpp rename to tests/chains/Tezos/PublicKeyTests.cpp diff --git a/tests/Tezos/SignerTests.cpp b/tests/chains/Tezos/SignerTests.cpp similarity index 100% rename from tests/Tezos/SignerTests.cpp rename to tests/chains/Tezos/SignerTests.cpp diff --git a/tests/Tezos/TWAnySignerTests.cpp b/tests/chains/Tezos/TWAnySignerTests.cpp similarity index 99% rename from tests/Tezos/TWAnySignerTests.cpp rename to tests/chains/Tezos/TWAnySignerTests.cpp index 4cac74a7885..89fac461677 100644 --- a/tests/Tezos/TWAnySignerTests.cpp +++ b/tests/chains/Tezos/TWAnySignerTests.cpp @@ -7,7 +7,7 @@ #include "HexCoding.h" #include "Tezos/BinaryCoding.h" #include "proto/Tezos.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Tezos/TWCoinTypeTests.cpp b/tests/chains/Tezos/TWCoinTypeTests.cpp similarity index 97% rename from tests/Tezos/TWCoinTypeTests.cpp rename to tests/chains/Tezos/TWCoinTypeTests.cpp index a8d44065d59..2c33d888f68 100644 --- a/tests/Tezos/TWCoinTypeTests.cpp +++ b/tests/chains/Tezos/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Theta/SignerTests.cpp b/tests/chains/Theta/SignerTests.cpp similarity index 100% rename from tests/Theta/SignerTests.cpp rename to tests/chains/Theta/SignerTests.cpp diff --git a/tests/Theta/TWAnySignerTests.cpp b/tests/chains/Theta/TWAnySignerTests.cpp similarity index 97% rename from tests/Theta/TWAnySignerTests.cpp rename to tests/chains/Theta/TWAnySignerTests.cpp index 6b9fbe87e6d..1c18296d549 100644 --- a/tests/Theta/TWAnySignerTests.cpp +++ b/tests/chains/Theta/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include "uint256.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Theta/TWCoinTypeTests.cpp b/tests/chains/Theta/TWCoinTypeTests.cpp similarity index 97% rename from tests/Theta/TWCoinTypeTests.cpp rename to tests/chains/Theta/TWCoinTypeTests.cpp index 55ed4e35b38..94107b0898c 100644 --- a/tests/Theta/TWCoinTypeTests.cpp +++ b/tests/chains/Theta/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Theta/TransactionTests.cpp b/tests/chains/Theta/TransactionTests.cpp similarity index 100% rename from tests/Theta/TransactionTests.cpp rename to tests/chains/Theta/TransactionTests.cpp diff --git a/tests/ThunderToken/TWCoinTypeTests.cpp b/tests/chains/ThunderToken/TWCoinTypeTests.cpp similarity index 97% rename from tests/ThunderToken/TWCoinTypeTests.cpp rename to tests/chains/ThunderToken/TWCoinTypeTests.cpp index 3b31872c8c3..c001ea07218 100644 --- a/tests/ThunderToken/TWCoinTypeTests.cpp +++ b/tests/chains/ThunderToken/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/TomoChain/TWCoinTypeTests.cpp b/tests/chains/TomoChain/TWCoinTypeTests.cpp similarity index 97% rename from tests/TomoChain/TWCoinTypeTests.cpp rename to tests/chains/TomoChain/TWCoinTypeTests.cpp index 0cb99362b04..4b6ec3810b4 100644 --- a/tests/TomoChain/TWCoinTypeTests.cpp +++ b/tests/chains/TomoChain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Tron/AddressTests.cpp b/tests/chains/Tron/AddressTests.cpp similarity index 100% rename from tests/Tron/AddressTests.cpp rename to tests/chains/Tron/AddressTests.cpp diff --git a/tests/Tron/SerializationTests.cpp b/tests/chains/Tron/SerializationTests.cpp similarity index 100% rename from tests/Tron/SerializationTests.cpp rename to tests/chains/Tron/SerializationTests.cpp diff --git a/tests/Tron/SignerTests.cpp b/tests/chains/Tron/SignerTests.cpp similarity index 100% rename from tests/Tron/SignerTests.cpp rename to tests/chains/Tron/SignerTests.cpp diff --git a/tests/Tron/TWAnySignerTests.cpp b/tests/chains/Tron/TWAnySignerTests.cpp similarity index 97% rename from tests/Tron/TWAnySignerTests.cpp rename to tests/chains/Tron/TWAnySignerTests.cpp index 0dc27c18583..ba7f2964e7b 100644 --- a/tests/Tron/TWAnySignerTests.cpp +++ b/tests/chains/Tron/TWAnySignerTests.cpp @@ -8,7 +8,7 @@ #include "proto/Tron.pb.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include namespace TW::Tron { diff --git a/tests/Tron/TWCoinTypeTests.cpp b/tests/chains/Tron/TWCoinTypeTests.cpp similarity index 97% rename from tests/Tron/TWCoinTypeTests.cpp rename to tests/chains/Tron/TWCoinTypeTests.cpp index e496477156e..b549e7bae86 100644 --- a/tests/Tron/TWCoinTypeTests.cpp +++ b/tests/chains/Tron/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/VeChain/SignerTests.cpp b/tests/chains/VeChain/SignerTests.cpp similarity index 100% rename from tests/VeChain/SignerTests.cpp rename to tests/chains/VeChain/SignerTests.cpp diff --git a/tests/VeChain/TWAnySignerTests.cpp b/tests/chains/VeChain/TWAnySignerTests.cpp similarity index 97% rename from tests/VeChain/TWAnySignerTests.cpp rename to tests/chains/VeChain/TWAnySignerTests.cpp index 783ecf827b6..992e65dbc51 100644 --- a/tests/VeChain/TWAnySignerTests.cpp +++ b/tests/chains/VeChain/TWAnySignerTests.cpp @@ -6,7 +6,7 @@ #include "HexCoding.h" #include "proto/VeChain.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/VeChain/TWCoinTypeTests.cpp b/tests/chains/VeChain/TWCoinTypeTests.cpp similarity index 97% rename from tests/VeChain/TWCoinTypeTests.cpp rename to tests/chains/VeChain/TWCoinTypeTests.cpp index cf41c6cba08..60d7e2b709c 100644 --- a/tests/VeChain/TWCoinTypeTests.cpp +++ b/tests/chains/VeChain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Viacoin/TWCoinTypeTests.cpp b/tests/chains/Viacoin/TWCoinTypeTests.cpp similarity index 97% rename from tests/Viacoin/TWCoinTypeTests.cpp rename to tests/chains/Viacoin/TWCoinTypeTests.cpp index 15ead27fdc1..3fcc86d7e37 100644 --- a/tests/Viacoin/TWCoinTypeTests.cpp +++ b/tests/chains/Viacoin/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Viacoin/TWViacoinAddressTests.cpp b/tests/chains/Viacoin/TWViacoinAddressTests.cpp similarity index 99% rename from tests/Viacoin/TWViacoinAddressTests.cpp rename to tests/chains/Viacoin/TWViacoinAddressTests.cpp index 55ef8fe74a5..d4ea5b115ea 100644 --- a/tests/Viacoin/TWViacoinAddressTests.cpp +++ b/tests/chains/Viacoin/TWViacoinAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Wanchain/TWCoinTypeTests.cpp b/tests/chains/Wanchain/TWCoinTypeTests.cpp similarity index 97% rename from tests/Wanchain/TWCoinTypeTests.cpp rename to tests/chains/Wanchain/TWCoinTypeTests.cpp index f3ba2ce8ee7..cc49f5ae5ae 100644 --- a/tests/Wanchain/TWCoinTypeTests.cpp +++ b/tests/chains/Wanchain/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Waves/AddressTests.cpp b/tests/chains/Waves/AddressTests.cpp similarity index 100% rename from tests/Waves/AddressTests.cpp rename to tests/chains/Waves/AddressTests.cpp diff --git a/tests/Waves/LeaseTests.cpp b/tests/chains/Waves/LeaseTests.cpp similarity index 100% rename from tests/Waves/LeaseTests.cpp rename to tests/chains/Waves/LeaseTests.cpp diff --git a/tests/Waves/SignerTests.cpp b/tests/chains/Waves/SignerTests.cpp similarity index 100% rename from tests/Waves/SignerTests.cpp rename to tests/chains/Waves/SignerTests.cpp diff --git a/tests/Waves/TWAnySignerTests.cpp b/tests/chains/Waves/TWAnySignerTests.cpp similarity index 97% rename from tests/Waves/TWAnySignerTests.cpp rename to tests/chains/Waves/TWAnySignerTests.cpp index bc57276977e..c814761a4f9 100644 --- a/tests/Waves/TWAnySignerTests.cpp +++ b/tests/chains/Waves/TWAnySignerTests.cpp @@ -7,7 +7,7 @@ #include "Base58.h" #include "HexCoding.h" #include "proto/Waves.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Waves/TWCoinTypeTests.cpp b/tests/chains/Waves/TWCoinTypeTests.cpp similarity index 97% rename from tests/Waves/TWCoinTypeTests.cpp rename to tests/chains/Waves/TWCoinTypeTests.cpp index b9043540b04..6460c3cbff0 100644 --- a/tests/Waves/TWCoinTypeTests.cpp +++ b/tests/chains/Waves/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Waves/TransactionTests.cpp b/tests/chains/Waves/TransactionTests.cpp similarity index 100% rename from tests/Waves/TransactionTests.cpp rename to tests/chains/Waves/TransactionTests.cpp diff --git a/tests/XRP/AddressTests.cpp b/tests/chains/XRP/AddressTests.cpp similarity index 100% rename from tests/XRP/AddressTests.cpp rename to tests/chains/XRP/AddressTests.cpp diff --git a/tests/XRP/TWAnySignerTests.cpp b/tests/chains/XRP/TWAnySignerTests.cpp similarity index 97% rename from tests/XRP/TWAnySignerTests.cpp rename to tests/chains/XRP/TWAnySignerTests.cpp index 6bf8bdf5986..1b9e05728e6 100644 --- a/tests/XRP/TWAnySignerTests.cpp +++ b/tests/chains/XRP/TWAnySignerTests.cpp @@ -9,7 +9,7 @@ #include "proto/Ripple.pb.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/XRP/TWCoinTypeTests.cpp b/tests/chains/XRP/TWCoinTypeTests.cpp similarity index 97% rename from tests/XRP/TWCoinTypeTests.cpp rename to tests/chains/XRP/TWCoinTypeTests.cpp index 5f4249ca2e1..1f1a967a712 100644 --- a/tests/XRP/TWCoinTypeTests.cpp +++ b/tests/chains/XRP/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/XRP/TWRippleAddressTests.cpp b/tests/chains/XRP/TWRippleAddressTests.cpp similarity index 97% rename from tests/XRP/TWRippleAddressTests.cpp rename to tests/chains/XRP/TWRippleAddressTests.cpp index 36e738592c1..6c21a4a4bc5 100644 --- a/tests/XRP/TWRippleAddressTests.cpp +++ b/tests/chains/XRP/TWRippleAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/XRP/TransactionTests.cpp b/tests/chains/XRP/TransactionTests.cpp similarity index 100% rename from tests/XRP/TransactionTests.cpp rename to tests/chains/XRP/TransactionTests.cpp diff --git a/tests/Zcash/AddressTests.cpp b/tests/chains/Zcash/AddressTests.cpp similarity index 100% rename from tests/Zcash/AddressTests.cpp rename to tests/chains/Zcash/AddressTests.cpp diff --git a/tests/Zcash/TWCoinTypeTests.cpp b/tests/chains/Zcash/TWCoinTypeTests.cpp similarity index 97% rename from tests/Zcash/TWCoinTypeTests.cpp rename to tests/chains/Zcash/TWCoinTypeTests.cpp index 475ffafd17b..46deb231511 100644 --- a/tests/Zcash/TWCoinTypeTests.cpp +++ b/tests/chains/Zcash/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Zcash/TWZcashAddressTests.cpp b/tests/chains/Zcash/TWZcashAddressTests.cpp similarity index 99% rename from tests/Zcash/TWZcashAddressTests.cpp rename to tests/chains/Zcash/TWZcashAddressTests.cpp index bbaa4ad932e..d3820059c5a 100644 --- a/tests/Zcash/TWZcashAddressTests.cpp +++ b/tests/chains/Zcash/TWZcashAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Zcash/TAddress.h" diff --git a/tests/Zcash/TWZcashTransactionTests.cpp b/tests/chains/Zcash/TWZcashTransactionTests.cpp similarity index 99% rename from tests/Zcash/TWZcashTransactionTests.cpp rename to tests/chains/Zcash/TWZcashTransactionTests.cpp index 96861a4a507..b0acaf0f132 100644 --- a/tests/Zcash/TWZcashTransactionTests.cpp +++ b/tests/chains/Zcash/TWZcashTransactionTests.cpp @@ -5,7 +5,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/Script.h" diff --git a/tests/Zelcash/TWCoinTypeTests.cpp b/tests/chains/Zelcash/TWCoinTypeTests.cpp similarity index 97% rename from tests/Zelcash/TWCoinTypeTests.cpp rename to tests/chains/Zelcash/TWCoinTypeTests.cpp index ee7bce7b346..29ff10fe24e 100644 --- a/tests/Zelcash/TWCoinTypeTests.cpp +++ b/tests/chains/Zelcash/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Zelcash/TWZelcashAddressTests.cpp b/tests/chains/Zelcash/TWZelcashAddressTests.cpp similarity index 99% rename from tests/Zelcash/TWZelcashAddressTests.cpp rename to tests/chains/Zelcash/TWZelcashAddressTests.cpp index 6d2b5807778..f78524d8898 100644 --- a/tests/Zelcash/TWZelcashAddressTests.cpp +++ b/tests/chains/Zelcash/TWZelcashAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Zelcash/TWZelcashTransactionTests.cpp b/tests/chains/Zelcash/TWZelcashTransactionTests.cpp similarity index 99% rename from tests/Zelcash/TWZelcashTransactionTests.cpp rename to tests/chains/Zelcash/TWZelcashTransactionTests.cpp index 90d2b09cb41..74f3413670e 100644 --- a/tests/Zelcash/TWZelcashTransactionTests.cpp +++ b/tests/chains/Zelcash/TWZelcashTransactionTests.cpp @@ -5,7 +5,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/OutPoint.h" #include "HexCoding.h" diff --git a/tests/Zilliqa/AddressTests.cpp b/tests/chains/Zilliqa/AddressTests.cpp similarity index 100% rename from tests/Zilliqa/AddressTests.cpp rename to tests/chains/Zilliqa/AddressTests.cpp diff --git a/tests/Zilliqa/SignatureTests.cpp b/tests/chains/Zilliqa/SignatureTests.cpp similarity index 97% rename from tests/Zilliqa/SignatureTests.cpp rename to tests/chains/Zilliqa/SignatureTests.cpp index cd3fe38e216..37684d1b11b 100644 --- a/tests/Zilliqa/SignatureTests.cpp +++ b/tests/chains/Zilliqa/SignatureTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "Data.h" #include diff --git a/tests/Zilliqa/SignerTests.cpp b/tests/chains/Zilliqa/SignerTests.cpp similarity index 100% rename from tests/Zilliqa/SignerTests.cpp rename to tests/chains/Zilliqa/SignerTests.cpp diff --git a/tests/Zilliqa/TWAnySignerTests.cpp b/tests/chains/Zilliqa/TWAnySignerTests.cpp similarity index 98% rename from tests/Zilliqa/TWAnySignerTests.cpp rename to tests/chains/Zilliqa/TWAnySignerTests.cpp index 876e6abec5b..acf610c7a29 100644 --- a/tests/Zilliqa/TWAnySignerTests.cpp +++ b/tests/chains/Zilliqa/TWAnySignerTests.cpp @@ -7,7 +7,7 @@ #include "HexCoding.h" #include "uint256.h" #include "proto/Zilliqa.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Zilliqa/TWCoinTypeTests.cpp b/tests/chains/Zilliqa/TWCoinTypeTests.cpp similarity index 97% rename from tests/Zilliqa/TWCoinTypeTests.cpp rename to tests/chains/Zilliqa/TWCoinTypeTests.cpp index 0b1d57ea02b..d0d51a694b2 100644 --- a/tests/Zilliqa/TWCoinTypeTests.cpp +++ b/tests/chains/Zilliqa/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Zilliqa/TWZilliqaAddressTests.cpp b/tests/chains/Zilliqa/TWZilliqaAddressTests.cpp similarity index 97% rename from tests/Zilliqa/TWZilliqaAddressTests.cpp rename to tests/chains/Zilliqa/TWZilliqaAddressTests.cpp index e2e19630cbe..2677af67c45 100644 --- a/tests/Zilliqa/TWZilliqaAddressTests.cpp +++ b/tests/chains/Zilliqa/TWZilliqaAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/ZkSyncV2/TWCoinTypeTests.cpp b/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp similarity index 97% rename from tests/ZkSyncV2/TWCoinTypeTests.cpp rename to tests/chains/ZkSyncV2/TWCoinTypeTests.cpp index 9b00c8112a6..95b4854e53e 100644 --- a/tests/ZkSyncV2/TWCoinTypeTests.cpp +++ b/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/xDai/TWCoinTypeTests.cpp b/tests/chains/xDai/TWCoinTypeTests.cpp similarity index 97% rename from tests/xDai/TWCoinTypeTests.cpp rename to tests/chains/xDai/TWCoinTypeTests.cpp index 05d70075d8d..c49ec7395f1 100644 --- a/tests/xDai/TWCoinTypeTests.cpp +++ b/tests/chains/xDai/TWCoinTypeTests.cpp @@ -8,7 +8,7 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/AnyAddressTests.cpp b/tests/common/AnyAddressTests.cpp similarity index 100% rename from tests/AnyAddressTests.cpp rename to tests/common/AnyAddressTests.cpp diff --git a/tests/BCSTests.cpp b/tests/common/BCSTests.cpp similarity index 100% rename from tests/BCSTests.cpp rename to tests/common/BCSTests.cpp diff --git a/tests/Base64Tests.cpp b/tests/common/Base64Tests.cpp similarity index 100% rename from tests/Base64Tests.cpp rename to tests/common/Base64Tests.cpp diff --git a/tests/BaseEncoding.cpp b/tests/common/BaseEncoding.cpp similarity index 100% rename from tests/BaseEncoding.cpp rename to tests/common/BaseEncoding.cpp diff --git a/tests/Bech32AddressTests.cpp b/tests/common/Bech32AddressTests.cpp similarity index 100% rename from tests/Bech32AddressTests.cpp rename to tests/common/Bech32AddressTests.cpp diff --git a/tests/Bech32Tests.cpp b/tests/common/Bech32Tests.cpp similarity index 100% rename from tests/Bech32Tests.cpp rename to tests/common/Bech32Tests.cpp diff --git a/tests/BinaryCodingTests.cpp b/tests/common/BinaryCodingTests.cpp similarity index 100% rename from tests/BinaryCodingTests.cpp rename to tests/common/BinaryCodingTests.cpp diff --git a/tests/CborTests.cpp b/tests/common/CborTests.cpp similarity index 100% rename from tests/CborTests.cpp rename to tests/common/CborTests.cpp diff --git a/tests/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp similarity index 100% rename from tests/CoinAddressDerivationTests.cpp rename to tests/common/CoinAddressDerivationTests.cpp diff --git a/tests/CoinAddressValidationTests.cpp b/tests/common/CoinAddressValidationTests.cpp similarity index 100% rename from tests/CoinAddressValidationTests.cpp rename to tests/common/CoinAddressValidationTests.cpp diff --git a/tests/DataTests.cpp b/tests/common/DataTests.cpp similarity index 100% rename from tests/DataTests.cpp rename to tests/common/DataTests.cpp diff --git a/tests/EncryptTests.cpp b/tests/common/EncryptTests.cpp similarity index 100% rename from tests/EncryptTests.cpp rename to tests/common/EncryptTests.cpp diff --git a/tests/HDWallet/HDWalletInternalTests.cpp b/tests/common/HDWallet/HDWalletInternalTests.cpp similarity index 99% rename from tests/HDWallet/HDWalletInternalTests.cpp rename to tests/common/HDWallet/HDWalletInternalTests.cpp index 10c7282a665..1f27ad22cca 100644 --- a/tests/HDWallet/HDWalletInternalTests.cpp +++ b/tests/common/HDWallet/HDWalletInternalTests.cpp @@ -11,7 +11,7 @@ #include "PublicKey.h" #include #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp similarity index 99% rename from tests/HDWallet/HDWalletTests.cpp rename to tests/common/HDWallet/HDWalletTests.cpp index 893eefb3a5f..9bc90832bed 100644 --- a/tests/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -15,7 +15,7 @@ #include "Hash.h" #include "Base58.h" #include "Coin.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include @@ -253,7 +253,7 @@ TEST(HDWallet, DeriveWithLeadingZerosEth) { } static nlohmann::json getVectors() { - const std::string vectorsJsonPath = std::string(TESTS_ROOT) + "/HDWallet/bip39_vectors.json"; + const std::string vectorsJsonPath = std::string(TESTS_ROOT) + "/common/HDWallet/bip39_vectors.json"; auto vectorsJson = loadJson(vectorsJsonPath)["english"]; return vectorsJson; } diff --git a/tests/HDWallet/bip39_vectors.json b/tests/common/HDWallet/bip39_vectors.json similarity index 100% rename from tests/HDWallet/bip39_vectors.json rename to tests/common/HDWallet/bip39_vectors.json diff --git a/tests/HashTests.cpp b/tests/common/HashTests.cpp similarity index 100% rename from tests/HashTests.cpp rename to tests/common/HashTests.cpp diff --git a/tests/HexCodingTests.cpp b/tests/common/HexCodingTests.cpp similarity index 100% rename from tests/HexCodingTests.cpp rename to tests/common/HexCodingTests.cpp diff --git a/tests/Keystore/Data/empty-accounts.json b/tests/common/Keystore/Data/empty-accounts.json similarity index 100% rename from tests/Keystore/Data/empty-accounts.json rename to tests/common/Keystore/Data/empty-accounts.json diff --git a/tests/Keystore/Data/ethereum-wallet-address-no-0x.json b/tests/common/Keystore/Data/ethereum-wallet-address-no-0x.json similarity index 100% rename from tests/Keystore/Data/ethereum-wallet-address-no-0x.json rename to tests/common/Keystore/Data/ethereum-wallet-address-no-0x.json diff --git a/tests/Keystore/Data/key.json b/tests/common/Keystore/Data/key.json similarity index 100% rename from tests/Keystore/Data/key.json rename to tests/common/Keystore/Data/key.json diff --git a/tests/Keystore/Data/key_bitcoin.json b/tests/common/Keystore/Data/key_bitcoin.json similarity index 100% rename from tests/Keystore/Data/key_bitcoin.json rename to tests/common/Keystore/Data/key_bitcoin.json diff --git a/tests/Keystore/Data/legacy-mnemonic.json b/tests/common/Keystore/Data/legacy-mnemonic.json similarity index 100% rename from tests/Keystore/Data/legacy-mnemonic.json rename to tests/common/Keystore/Data/legacy-mnemonic.json diff --git a/tests/Keystore/Data/legacy-private-key.json b/tests/common/Keystore/Data/legacy-private-key.json similarity index 100% rename from tests/Keystore/Data/legacy-private-key.json rename to tests/common/Keystore/Data/legacy-private-key.json diff --git a/tests/Keystore/Data/livepeer.json b/tests/common/Keystore/Data/livepeer.json similarity index 100% rename from tests/Keystore/Data/livepeer.json rename to tests/common/Keystore/Data/livepeer.json diff --git a/tests/Keystore/Data/missing-address.json b/tests/common/Keystore/Data/missing-address.json similarity index 100% rename from tests/Keystore/Data/missing-address.json rename to tests/common/Keystore/Data/missing-address.json diff --git a/tests/Keystore/Data/myetherwallet.uu b/tests/common/Keystore/Data/myetherwallet.uu similarity index 100% rename from tests/Keystore/Data/myetherwallet.uu rename to tests/common/Keystore/Data/myetherwallet.uu diff --git a/tests/Keystore/Data/pbkdf2.json b/tests/common/Keystore/Data/pbkdf2.json similarity index 100% rename from tests/Keystore/Data/pbkdf2.json rename to tests/common/Keystore/Data/pbkdf2.json diff --git a/tests/Keystore/Data/wallet.json b/tests/common/Keystore/Data/wallet.json similarity index 100% rename from tests/Keystore/Data/wallet.json rename to tests/common/Keystore/Data/wallet.json diff --git a/tests/Keystore/Data/watch.json b/tests/common/Keystore/Data/watch.json similarity index 100% rename from tests/Keystore/Data/watch.json rename to tests/common/Keystore/Data/watch.json diff --git a/tests/Keystore/Data/web3j.json b/tests/common/Keystore/Data/web3j.json similarity index 100% rename from tests/Keystore/Data/web3j.json rename to tests/common/Keystore/Data/web3j.json diff --git a/tests/Keystore/DerivationPathTests.cpp b/tests/common/Keystore/DerivationPathTests.cpp similarity index 100% rename from tests/Keystore/DerivationPathTests.cpp rename to tests/common/Keystore/DerivationPathTests.cpp diff --git a/tests/Keystore/StoredKeyTests.cpp b/tests/common/Keystore/StoredKeyTests.cpp similarity index 95% rename from tests/Keystore/StoredKeyTests.cpp rename to tests/common/Keystore/StoredKeyTests.cpp index e792c0f7ab5..51cc959968d 100644 --- a/tests/Keystore/StoredKeyTests.cpp +++ b/tests/common/Keystore/StoredKeyTests.cpp @@ -18,7 +18,7 @@ extern std::string TESTS_ROOT; -namespace TW::Keystore { +namespace TW::Keystore::tests { using namespace std; @@ -31,6 +31,10 @@ const TWCoinType coinTypeBsc = TWCoinTypeSmartChain; const TWCoinType coinTypeEth = TWCoinTypeEthereum; const TWCoinType coinTypeBscLegacy = TWCoinTypeSmartChainLegacy; +const std::string testDataPath(const char* subpath) { + return TESTS_ROOT + "/common/Keystore/Data/" + subpath; +} + TEST(StoredKey, CreateWithMnemonic) { auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelDefault); EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); @@ -272,11 +276,11 @@ TEST(StoredKey, WalletInvalid) { } TEST(StoredKey, LoadNonexistent) { - ASSERT_THROW(StoredKey::load(TESTS_ROOT + "/Keystore/Data/nonexistent.json"), invalid_argument); + ASSERT_THROW(StoredKey::load(testDataPath("nonexistent.json")), invalid_argument); } TEST(StoredKey, LoadLegacyPrivateKey) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/legacy-private-key.json"); + const auto key = StoredKey::load(testDataPath("legacy-private-key.json")); EXPECT_EQ(key.type, StoredKeyType::privateKey); EXPECT_EQ(key.id, "3051ca7d-3d36-4a4a-acc2-09e9083732b0"); EXPECT_EQ(key.accounts[0].coin, TWCoinTypeEthereum); @@ -284,7 +288,7 @@ TEST(StoredKey, LoadLegacyPrivateKey) { } TEST(StoredKey, LoadLivepeerKey) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/livepeer.json"); + const auto key = StoredKey::load(testDataPath("livepeer.json")); EXPECT_EQ(key.type, StoredKeyType::privateKey); EXPECT_EQ(key.id, "70ea3601-ee21-4e94-a7e4-66255a987d22"); EXPECT_EQ(key.accounts[0].coin, TWCoinTypeEthereum); @@ -292,7 +296,7 @@ TEST(StoredKey, LoadLivepeerKey) { } TEST(StoredKey, LoadPBKDF2Key) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/pbkdf2.json"); + const auto key = StoredKey::load(testDataPath("pbkdf2.json")); EXPECT_EQ(key.type, StoredKeyType::privateKey); EXPECT_EQ(key.id, "3198bc9c-6672-5ab3-d995-4942343ae5b6"); @@ -306,7 +310,7 @@ TEST(StoredKey, LoadPBKDF2Key) { } TEST(StoredKey, LoadLegacyMnemonic) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/legacy-mnemonic.json"); + const auto key = StoredKey::load(testDataPath("legacy-mnemonic.json")); EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); EXPECT_EQ(key.id, "629aad29-0b22-488e-a0e7-b4219d4f311c"); @@ -324,7 +328,7 @@ TEST(StoredKey, LoadLegacyMnemonic) { } TEST(StoredKey, LoadFromWeb3j) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/web3j.json"); + const auto key = StoredKey::load(testDataPath("web3j.json")); EXPECT_EQ(key.type, StoredKeyType::privateKey); EXPECT_EQ(key.id, "86066d8c-8dba-4d81-afd4-934e2a2b72a2"); const auto password = parse_hex("2d6eefbfbd4622efbfbdefbfbd516718efbfbdefbfbdefbfbdefbfbd59efbfbd30efbfbdefbfbd3a4348efbfbd2aefbfbdefbfbd49efbfbd27efbfbd0638efbfbdefbfbdefbfbd4cefbfbd6befbfbdefbfbd6defbfbdefbfbd63efbfbd5aefbfbd61262b70efbfbdefbfbdefbfbdefbfbdefbfbdc7aa373163417cefbfbdefbfbdefbfbd44efbfbdefbfbd1d10efbfbdefbfbdefbfbd61dc9e5b124befbfbd11efbfbdefbfbd2fefbfbdefbfbd3d7c574868efbfbdefbfbdefbfbd37043b7b5c1a436471592f02efbfbd18efbfbdefbfbd2befbfbdefbfbd7218efbfbd6a68efbfbdcb8e5f3328773ec48174efbfbd67efbfbdefbfbdefbfbdefbfbdefbfbd2a31efbfbd7f60efbfbdd884efbfbd57efbfbd25efbfbd590459efbfbd37efbfbd2bdca20fefbfbdefbfbdefbfbdefbfbd39450113efbfbdefbfbdefbfbd454671efbfbdefbfbdd49fefbfbd47efbfbdefbfbdefbfbdefbfbd00efbfbdefbfbdefbfbdefbfbd05203f4c17712defbfbd7bd1bbdc967902efbfbdc98a77efbfbd707a36efbfbd12efbfbdefbfbd57c78cefbfbdefbfbdefbfbd10efbfbdefbfbdefbfbde1a1bb08efbfbdefbfbd26efbfbdefbfbd58efbfbdefbfbdc4b1efbfbd295fefbfbd0eefbfbdefbfbdefbfbd0e6eefbfbd"); @@ -333,7 +337,7 @@ TEST(StoredKey, LoadFromWeb3j) { } TEST(StoredKey, ReadWallet) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key.json"); + const auto key = StoredKey::load(testDataPath("key.json")); EXPECT_EQ(key.type, StoredKeyType::privateKey); EXPECT_EQ(key.id, "e13b209c-3b2f-4327-bab0-3bef2e51630d"); @@ -355,23 +359,23 @@ TEST(StoredKey, ReadWallet) { } TEST(StoredKey, ReadMyEtherWallet) { - ASSERT_NO_THROW(StoredKey::load(TESTS_ROOT + "/Keystore/Data/myetherwallet.uu")); + ASSERT_NO_THROW(StoredKey::load(testDataPath("myetherwallet.uu"))); } TEST(StoredKey, InvalidPassword) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key.json"); + const auto key = StoredKey::load(testDataPath("key.json")); ASSERT_THROW(key.payload.decrypt(gPassword), DecryptionError); } TEST(StoredKey, EmptyAccounts) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/empty-accounts.json"); + const auto key = StoredKey::load(testDataPath("empty-accounts.json")); ASSERT_NO_THROW(key.payload.decrypt(TW::data("testpassword"))); } TEST(StoredKey, Decrypt) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key.json"); + const auto key = StoredKey::load(testDataPath("key.json")); const auto privateKey = key.payload.decrypt(TW::data("testpassword")); EXPECT_EQ(hex(privateKey), "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"); @@ -400,19 +404,19 @@ TEST(StoredKey, CreateAccounts) { } TEST(StoredKey, DecodingEthereumAddress) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key.json"); + const auto key = StoredKey::load(testDataPath("key.json")); EXPECT_EQ(key.accounts[0].address, "0x008AeEda4D805471dF9b2A5B0f38A0C3bCBA786b"); } TEST(StoredKey, DecodingBitcoinAddress) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key_bitcoin.json"); + const auto key = StoredKey::load(testDataPath("key_bitcoin.json")); EXPECT_EQ(key.accounts[0].address, "3PWazDi9n1Hfyq9gXFxDxzADNL8RNYyK2y"); } TEST(StoredKey, RemoveAccount) { - auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/legacy-mnemonic.json"); + auto key = StoredKey::load(testDataPath("legacy-mnemonic.json")); EXPECT_EQ(key.accounts.size(), 2ul); key.removeAccount(TWCoinTypeEthereum); EXPECT_EQ(key.accounts.size(), 1ul); @@ -420,7 +424,7 @@ TEST(StoredKey, RemoveAccount) { } TEST(StoredKey, MissingAddressFix) { - auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/missing-address.json"); + auto key = StoredKey::load(testDataPath("missing-address.json")); EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); const auto wallet = key.wallet(gPassword); @@ -439,7 +443,7 @@ TEST(StoredKey, MissingAddressFix) { } TEST(StoredKey, MissingAddressReadd) { - auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/missing-address.json"); + auto key = StoredKey::load(testDataPath("missing-address.json")); EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); const auto wallet = key.wallet(gPassword); @@ -460,7 +464,7 @@ TEST(StoredKey, MissingAddressReadd) { } TEST(StoredKey, EtherWalletAddressNo0x) { - auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/ethereum-wallet-address-no-0x.json"); + auto key = StoredKey::load(testDataPath("ethereum-wallet-address-no-0x.json")); key.fixAddresses(TW::data("15748c4e3dca6ae2110535576ab0c398cb79d985707c68ee6c9f9df9d421dd53")); const auto account = key.account(TWCoinTypeEthereum, nullptr); EXPECT_EQ(account->address, "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309"); diff --git a/tests/MnemonicTests.cpp b/tests/common/MnemonicTests.cpp similarity index 100% rename from tests/MnemonicTests.cpp rename to tests/common/MnemonicTests.cpp diff --git a/tests/NumericLiteralTests.cpp b/tests/common/NumericLiteralTests.cpp similarity index 100% rename from tests/NumericLiteralTests.cpp rename to tests/common/NumericLiteralTests.cpp diff --git a/tests/PrivateKeyTests.cpp b/tests/common/PrivateKeyTests.cpp similarity index 100% rename from tests/PrivateKeyTests.cpp rename to tests/common/PrivateKeyTests.cpp diff --git a/tests/PublicKeyTests.cpp b/tests/common/PublicKeyTests.cpp similarity index 99% rename from tests/PublicKeyTests.cpp rename to tests/common/PublicKeyTests.cpp index a9e2f09d115..1dae03fb237 100644 --- a/tests/PublicKeyTests.cpp +++ b/tests/common/PublicKeyTests.cpp @@ -9,7 +9,7 @@ #include "Hash.h" #include "HexCoding.h" #include "PrivateKey.h" -#include "interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/interface/TWTestUtilities.cpp b/tests/common/TestUtilities.cpp similarity index 96% rename from tests/interface/TWTestUtilities.cpp rename to tests/common/TestUtilities.cpp index 7a7fae7b15a..76e34fd35f1 100644 --- a/tests/interface/TWTestUtilities.cpp +++ b/tests/common/TestUtilities.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include #include diff --git a/tests/interface/TWTestUtilities.h b/tests/common/TestUtilities.h similarity index 100% rename from tests/interface/TWTestUtilities.h rename to tests/common/TestUtilities.h diff --git a/tests/TransactionCompilerTests.cpp b/tests/common/TransactionCompilerTests.cpp similarity index 99% rename from tests/TransactionCompilerTests.cpp rename to tests/common/TransactionCompilerTests.cpp index 9590eb07ef6..e2d26f1d07d 100644 --- a/tests/TransactionCompilerTests.cpp +++ b/tests/common/TransactionCompilerTests.cpp @@ -23,7 +23,7 @@ #include "uint256.h" #include -#include "interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Uint256Tests.cpp b/tests/common/Uint256Tests.cpp similarity index 100% rename from tests/Uint256Tests.cpp rename to tests/common/Uint256Tests.cpp diff --git a/tests/WalletConsoleTests.cpp b/tests/common/WalletConsoleTests.cpp similarity index 100% rename from tests/WalletConsoleTests.cpp rename to tests/common/WalletConsoleTests.cpp diff --git a/tests/algorithm/erase_tests.cpp b/tests/common/algorithm/erase_tests.cpp similarity index 100% rename from tests/algorithm/erase_tests.cpp rename to tests/common/algorithm/erase_tests.cpp diff --git a/tests/algorithm/sort_copy_tests.cpp b/tests/common/algorithm/sort_copy_tests.cpp similarity index 100% rename from tests/algorithm/sort_copy_tests.cpp rename to tests/common/algorithm/sort_copy_tests.cpp diff --git a/tests/algorithm/to_array_tests.cpp b/tests/common/algorithm/to_array_tests.cpp similarity index 100% rename from tests/algorithm/to_array_tests.cpp rename to tests/common/algorithm/to_array_tests.cpp diff --git a/tests/memory/memzero_tests.cpp b/tests/common/memory/memzero_tests.cpp similarity index 100% rename from tests/memory/memzero_tests.cpp rename to tests/common/memory/memzero_tests.cpp diff --git a/tests/operators/equality_comparable_tests.cpp b/tests/common/operators/equality_comparable_tests.cpp similarity index 100% rename from tests/operators/equality_comparable_tests.cpp rename to tests/common/operators/equality_comparable_tests.cpp diff --git a/tests/interface/TWAESTests.cpp b/tests/interface/TWAESTests.cpp index 8f1229ae5ca..0ee04dfdc5b 100644 --- a/tests/interface/TWAESTests.cpp +++ b/tests/interface/TWAESTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include diff --git a/tests/interface/TWAccountTests.cpp b/tests/interface/TWAccountTests.cpp index cfcf75d3ce9..5a8bcf4661d 100644 --- a/tests/interface/TWAccountTests.cpp +++ b/tests/interface/TWAccountTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include #include diff --git a/tests/interface/TWAnyAddressTests.cpp b/tests/interface/TWAnyAddressTests.cpp index bf6e4e98c42..c0bef30ab93 100644 --- a/tests/interface/TWAnyAddressTests.cpp +++ b/tests/interface/TWAnyAddressTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include "HexCoding.h" #include diff --git a/tests/interface/TWBase32Tests.cpp b/tests/interface/TWBase32Tests.cpp index c8d826dad86..a8582fa23f5 100644 --- a/tests/interface/TWBase32Tests.cpp +++ b/tests/interface/TWBase32Tests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include "Data.h" #include diff --git a/tests/interface/TWBase58Tests.cpp b/tests/interface/TWBase58Tests.cpp index 79542447036..f2c35aa11fe 100644 --- a/tests/interface/TWBase58Tests.cpp +++ b/tests/interface/TWBase58Tests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include diff --git a/tests/interface/TWBase64Tests.cpp b/tests/interface/TWBase64Tests.cpp index d3da4e2f747..fc5332dfdd5 100644 --- a/tests/interface/TWBase64Tests.cpp +++ b/tests/interface/TWBase64Tests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include "Data.h" #include diff --git a/tests/interface/TWCoinTypeTests.cpp b/tests/interface/TWCoinTypeTests.cpp index 0390b8ad1e0..5fd55733ef8 100644 --- a/tests/interface/TWCoinTypeTests.cpp +++ b/tests/interface/TWCoinTypeTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include diff --git a/tests/interface/TWDataTests.cpp b/tests/interface/TWDataTests.cpp index 73c287d5a2e..015c5e12b00 100644 --- a/tests/interface/TWDataTests.cpp +++ b/tests/interface/TWDataTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #include -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/interface/TWDataVectorTests.cpp b/tests/interface/TWDataVectorTests.cpp index 38779f4bb92..36a984c2e2c 100644 --- a/tests/interface/TWDataVectorTests.cpp +++ b/tests/interface/TWDataVectorTests.cpp @@ -6,7 +6,7 @@ #include #include "HexCoding.h" -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/interface/TWDerivationPathTests.cpp b/tests/interface/TWDerivationPathTests.cpp index 22cef1e8b32..15db6b2645e 100644 --- a/tests/interface/TWDerivationPathTests.cpp +++ b/tests/interface/TWDerivationPathTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include "TrustWalletCore/TWDerivation.h" #include "TrustWalletCore/TWPurpose.h" #include diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index c826186dbfd..63fc42fec3c 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include "Coin.h" diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index a4a205cb75a..a24bda956ff 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include #include diff --git a/tests/interface/TWHashTests.cpp b/tests/interface/TWHashTests.cpp index 09b4498c690..e67d0aeb36f 100644 --- a/tests/interface/TWHashTests.cpp +++ b/tests/interface/TWHashTests.cpp @@ -9,7 +9,7 @@ #include -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace std; diff --git a/tests/interface/TWMnemonicTests.cpp b/tests/interface/TWMnemonicTests.cpp index 6ed7d3b1a9c..df507c553a9 100644 --- a/tests/interface/TWMnemonicTests.cpp +++ b/tests/interface/TWMnemonicTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include diff --git a/tests/interface/TWPBKDF2Tests.cpp b/tests/interface/TWPBKDF2Tests.cpp index 646126fb3de..57c48c73083 100644 --- a/tests/interface/TWPBKDF2Tests.cpp +++ b/tests/interface/TWPBKDF2Tests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include diff --git a/tests/interface/TWPrivateKeyTests.cpp b/tests/interface/TWPrivateKeyTests.cpp index 96df973cf57..2ce9d6be10c 100644 --- a/tests/interface/TWPrivateKeyTests.cpp +++ b/tests/interface/TWPrivateKeyTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include "PrivateKey.h" #include "PublicKey.h" diff --git a/tests/interface/TWPublicKeyTests.cpp b/tests/interface/TWPublicKeyTests.cpp index 5fd8fbd7af8..64d712cc96a 100644 --- a/tests/interface/TWPublicKeyTests.cpp +++ b/tests/interface/TWPublicKeyTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include "PublicKey.h" #include "PrivateKey.h" diff --git a/tests/interface/TWStoredKeyTests.cpp b/tests/interface/TWStoredKeyTests.cpp index 9c34ea41a89..6cfa96c04c8 100644 --- a/tests/interface/TWStoredKeyTests.cpp +++ b/tests/interface/TWStoredKeyTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include #include @@ -41,7 +41,7 @@ struct std::shared_ptr createDefaultStoredKey() { } TEST(TWStoredKey, loadPBKDF2Key) { - const auto filename = WRAPS(TWStringCreateWithUTF8Bytes((TESTS_ROOT + "/Keystore/Data/pbkdf2.json").c_str())); + const auto filename = WRAPS(TWStringCreateWithUTF8Bytes((TESTS_ROOT + "/common/Keystore/Data/pbkdf2.json").c_str())); const auto key = WRAP(TWStoredKey, TWStoredKeyLoad(filename.get())); const auto keyId = WRAPS(TWStoredKeyIdentifier(key.get())); EXPECT_EQ(string(TWStringUTF8Bytes(keyId.get())), "3198bc9c-6672-5ab3-d995-4942343ae5b6"); diff --git a/tests/interface/TWStringTests.cpp b/tests/interface/TWStringTests.cpp index 17d800b6782..e79755ba76d 100644 --- a/tests/interface/TWStringTests.cpp +++ b/tests/interface/TWStringTests.cpp @@ -4,7 +4,7 @@ // 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 "TestUtilities.h" #include diff --git a/tests/interface/TWTransactionCompilerTests.cpp b/tests/interface/TWTransactionCompilerTests.cpp index 20cab4a754e..fa97c94b719 100644 --- a/tests/interface/TWTransactionCompilerTests.cpp +++ b/tests/interface/TWTransactionCompilerTests.cpp @@ -22,7 +22,7 @@ #include "uint256.h" #include -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tools/generate-files b/tools/generate-files index 54c041b5fbe..aa694add2cc 100755 --- a/tools/generate-files +++ b/tools/generate-files @@ -63,7 +63,7 @@ fi "$PROTOC" -I=$PREFIX/include -I=src/Tron/Protobuf --cpp_out=src/Tron/Protobuf src/Tron/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Zilliqa/Protobuf --cpp_out=src/Zilliqa/Protobuf src/Zilliqa/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Cosmos/Protobuf --cpp_out=src/Cosmos/Protobuf src/Cosmos/Protobuf/*.proto -"$PROTOC" -I=$PREFIX/include -I=tests/Cosmos/Protobuf --cpp_out=tests/Cosmos/Protobuf tests/Cosmos/Protobuf/*.proto +"$PROTOC" -I=$PREFIX/include -I=tests/chains/Cosmos/Protobuf --cpp_out=tests/chains/Cosmos/Protobuf tests/chains/Cosmos/Protobuf/*.proto # Generate Proto interface file "$PROTOC" -I=$PREFIX/include -I=src/proto --plugin=$PREFIX/bin/protoc-gen-c-typedef --c-typedef_out include/TrustWalletCore src/proto/*.proto From dd3a474e31518a289991d198503870fd944e98bc Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 12 Oct 2022 14:32:35 +0200 Subject: [PATCH 015/426] [Cosmos]: add voting capabilities (#2638) --- src/Cosmos/Protobuf/gov_tx.proto | 24 +++++++++++++++++++ src/Cosmos/ProtobufSerialization.cpp | 35 ++++++++++++++++++++++++++++ src/proto/Cosmos.proto | 22 +++++++++++++++++ tests/chains/Cosmos/SignerTests.cpp | 30 ++++++++++++++++++++++++ 4 files changed, 111 insertions(+) create mode 100644 src/Cosmos/Protobuf/gov_tx.proto diff --git a/src/Cosmos/Protobuf/gov_tx.proto b/src/Cosmos/Protobuf/gov_tx.proto new file mode 100644 index 00000000000..4f018a37e93 --- /dev/null +++ b/src/Cosmos/Protobuf/gov_tx.proto @@ -0,0 +1,24 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.gov.v1beta1; + +// VoteOption enumerates the valid vote options for a given governance proposal. +enum VoteOption { + // VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + VOTE_OPTION_UNSPECIFIED = 0; + // VOTE_OPTION_YES defines a yes vote option. + VOTE_OPTION_YES = 1; + // VOTE_OPTION_ABSTAIN defines an abstain vote option. + VOTE_OPTION_ABSTAIN = 2; + // VOTE_OPTION_NO defines a no vote option. + VOTE_OPTION_NO = 3; + // VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + VOTE_OPTION_NO_WITH_VETO = 4; +} + +// MsgVote defines a message to cast a vote. +message MsgVote { + uint64 proposal_id = 1; + string voter = 2; + VoteOption option = 3; +} diff --git a/src/Cosmos/ProtobufSerialization.cpp b/src/Cosmos/ProtobufSerialization.cpp index a59f64463c0..e2f2e070890 100644 --- a/src/Cosmos/ProtobufSerialization.cpp +++ b/src/Cosmos/ProtobufSerialization.cpp @@ -14,6 +14,7 @@ #include "Protobuf/staking_tx.pb.h" #include "Protobuf/authz_tx.pb.h" #include "Protobuf/tx.pb.h" +#include "Protobuf/gov_tx.pb.h" #include "Protobuf/crypto_secp256k1_keys.pb.h" #include "Protobuf/ibc_applications_transfer_tx.pb.h" #include "Protobuf/terra_wasm_v1beta1_tx.pb.h" @@ -254,6 +255,40 @@ google::protobuf::Any convertMessage(const Proto::Message& msg) { any.PackFrom(msgAuthRevoke, ProtobufAnyNamespacePrefix); return any; } + case Proto::Message::kMsgVote: { + assert(msg.has_msg_vote()); + const auto& vote = msg.msg_vote(); + auto msgVote = cosmos::gov::v1beta1::MsgVote(); + // LCOV_EXCL_START + switch (vote.option()) { + case Proto::Message_VoteOption__UNSPECIFIED: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_UNSPECIFIED); + break; + case Proto::Message_VoteOption_YES: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_YES); + break; + case Proto::Message_VoteOption_ABSTAIN: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_ABSTAIN); + break; + case Proto::Message_VoteOption_NO: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_NO); + break; + case Proto::Message_VoteOption_NO_WITH_VETO: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_NO_WITH_VETO); + break; + case Proto::Message_VoteOption_Message_VoteOption_INT_MIN_SENTINEL_DO_NOT_USE_: + msgVote.set_option(cosmos::gov::v1beta1::VoteOption_INT_MIN_SENTINEL_DO_NOT_USE_); + break; + case Proto::Message_VoteOption_Message_VoteOption_INT_MAX_SENTINEL_DO_NOT_USE_: + msgVote.set_option(cosmos::gov::v1beta1::VoteOption_INT_MAX_SENTINEL_DO_NOT_USE_); + break; + } + // LCOV_EXCL_STOP + msgVote.set_proposal_id(vote.proposal_id()); + msgVote.set_voter(vote.voter()); + any.PackFrom(msgVote, ProtobufAnyNamespacePrefix); + return any; + } default: throw std::invalid_argument(std::string("Message not supported ") + std::to_string(msg.message_oneof_case())); diff --git a/src/proto/Cosmos.proto b/src/proto/Cosmos.proto index e95b00daa37..97c8bb2e358 100644 --- a/src/proto/Cosmos.proto +++ b/src/proto/Cosmos.proto @@ -270,6 +270,27 @@ message Message { string msg_type_url = 3; } + // VoteOption enumerates the valid vote options for a given governance proposal. + enum VoteOption { + //_UNSPECIFIED defines a no-op vote option. + _UNSPECIFIED = 0; + // YES defines a yes vote option. + YES = 1; + // ABSTAIN defines an abstain vote option. + ABSTAIN = 2; + // NO defines a no vote option. + NO = 3; + // NO_WITH_VETO defines a no with veto vote option. + NO_WITH_VETO = 4; + } + + // cosmos-sdk/MsgVote defines a message to cast a vote. + message MsgVote { + uint64 proposal_id = 1; + string voter = 2; + VoteOption option = 3; + } + // The payload message oneof message_oneof { Send send_coins_message = 1; @@ -289,6 +310,7 @@ message Message { SignDirect sign_direct_message = 15; AuthGrant auth_grant = 16; AuthRevoke auth_revoke = 17; + MsgVote msg_vote = 18; } } diff --git a/tests/chains/Cosmos/SignerTests.cpp b/tests/chains/Cosmos/SignerTests.cpp index a791e7bd10f..d9202607cc0 100644 --- a/tests/chains/Cosmos/SignerTests.cpp +++ b/tests/chains/Cosmos/SignerTests.cpp @@ -292,4 +292,34 @@ TEST(CosmosSigner, SignDirect_0a90010a) { EXPECT_EQ(output.error(), ""); } +TEST(CosmosSigner, MsgVote) { + // Successfully broadcasted https://www.mintscan.io/cosmos/txs/2EFA054B842B1641B131137B13360F95164C6C1D51BB4A4AC6DE8F75F504AA4C + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1366160); + input.set_chain_id("cosmoshub-4"); + input.set_memo(""); + input.set_sequence(0); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_msg_vote(); + message.set_voter("cosmos1mry47pkga5tdswtluy0m8teslpalkdq07pswu4"); + message.set_proposal_id(77); + message.set_option(TW::Cosmos::Proto::Message_VoteOption_YES); + + auto& fee = *input.mutable_fee(); + fee.set_gas(97681); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uatom"); + amountOfFee->set_amount("2418"); + + auto privateKey = parse_hex("a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + auto expected = R"( + {"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"ClQKUgobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjMITRItY29zbW9zMW1yeTQ3cGtnYTV0ZHN3dGx1eTBtOHRlc2xwYWxrZHEwN3Bzd3U0GAESZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAsv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarlEgQKAggBEhMKDQoFdWF0b20SBDI0MTgQkfsFGkA+Nb3NULc38quGC1x+8ZXry4w9mMX3IA7wUjFboTv7kVOwPlleIc8UqIsjVvKTUFnUuW8dlGQzNR1KkvbvZ1NA"})"; + assertJSONEqual(output.serialized(), expected); +} + } // namespace TW::Cosmos::tests From 5e0319e2e6e697b9f416198408b30c747e1a6b45 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Wed, 12 Oct 2022 16:55:16 +0200 Subject: [PATCH 016/426] Constant time operation in Mnemonic::isValidWord (#2636) --- src/Mnemonic.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Mnemonic.cpp b/src/Mnemonic.cpp index 08a3112ec2a..1bff4d76ea7 100644 --- a/src/Mnemonic.cpp +++ b/src/Mnemonic.cpp @@ -28,16 +28,15 @@ inline const char* const* mnemonicWordlist() { return wordlist; } bool Mnemonic::isValidWord(const std::string& word) { const char* wordC = word.c_str(); const auto len = word.length(); + // Although this operation is not security-critical, we aim for constant-time operation here as well + // (i.e., no early exit on match) + auto found = false; for (const char* const* w = mnemonicWordlist(); *w != nullptr; ++w) { - if (strlen(*w) != len) { - continue; - } - if (strncmp(*w, wordC, len) == 0) { - return true; + if (strlen(*w) == len && strncmp(*w, wordC, len) == 0) { + found = true; } } - // not found - return false; + return found; } std::string Mnemonic::suggest(const std::string& prefix) { From d4fabe72f8ea782f69ed346695a3e431c5e561cb Mon Sep 17 00:00:00 2001 From: lolcathost <115231640+lolcathost@users.noreply.github.com> Date: Wed, 12 Oct 2022 19:54:53 +0400 Subject: [PATCH 017/426] add own implementation of crc32 checksum, remove boost dependancy (#2629) Co-authored-by: Angelo Laub --- src/Crc.cpp | 24 +++++++++--------------- src/Crc.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/Crc.cpp b/src/Crc.cpp index 37f4ea8ad6c..1cbbe6d2696 100644 --- a/src/Crc.cpp +++ b/src/Crc.cpp @@ -6,8 +6,7 @@ #include "Crc.h" -#include // for boost::crc_32_type - +#include #include using namespace TW; @@ -32,17 +31,12 @@ uint16_t Crc::crc16(uint8_t* bytes, uint32_t length) { return crc & 0xffff; } -uint32_t Crc::crc32(const Data& data) -{ - boost::crc_32_type result; - result.process_bytes((const void*)data.data(), data.size()); - return (uint32_t)result.checksum(); -} - -uint32_t Crc::crc32C(const Data& data) -{ - using crc_32c_type = boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true>; - crc_32c_type result; - result.process_bytes((const void*)data.data(), data.size()); - return (uint32_t)result.checksum(); +// Algorithm inspired by this old-style C implementation: +// https://web.mit.edu/freebsd/head/sys/libkern/crc32.c (Public Domain code) +uint32_t Crc::crc32(const Data& data) { + uint32_t c = std::numeric_limits::max(); + for (const auto byte : data) { + c = crc32_table[(c ^ byte) & 0xFF] ^ (c >> 8); + } + return ~c; } diff --git a/src/Crc.h b/src/Crc.h index 88bb3b28854..1180035491b 100644 --- a/src/Crc.h +++ b/src/Crc.h @@ -17,6 +17,50 @@ uint16_t crc16(uint8_t* bytes, uint32_t length); uint32_t crc32(const TW::Data& data); -uint32_t crc32C(const TW::Data& data); - +// Table taken from https://web.mit.edu/freebsd/head/sys/libkern/crc32.c (Public Domain code) +// This table is used to speed up the crc calculation. +static constexpr uint32_t crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; } // namespace TW::Crc From 485e66dbf54e9b532c635601defaf384916c88a5 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 13 Oct 2022 11:16:04 +0200 Subject: [PATCH 018/426] [Aptos]: Transfer V2, automatic account creation (#2644) --- .../app/blockchains/aptos/TestAptosSigner.kt | 12 ++-- src/Aptos/MoveTypes.h | 4 ++ src/Aptos/Signer.cpp | 20 +++--- swift/Tests/Blockchains/AptosTests.swift | 12 ++-- tests/chains/Aptos/SignerTests.cpp | 61 +++---------------- tests/chains/Aptos/TWAnySignerTests.cpp | 22 +++---- 6 files changed, 44 insertions(+), 87 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt index 32df8c8e33e..6d52a4c9d1d 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt @@ -25,15 +25,15 @@ class TestAptosSigner { @Test fun AptosTransactionSigning() { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xbb3b3c33781c27e486afa2db854fb0a5c846d0967672feb2c6c3297a2b14e1ce?network=Devnet + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet val key = "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec".toHexBytesInByteString() val transfer = Aptos.TransferMessage.newBuilder().setAmount(1000) .setTo("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").build() - val signingInput = Aptos.SigningInput.newBuilder().setChainId(32) + val signingInput = Aptos.SigningInput.newBuilder().setChainId(33) .setSender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30") - .setSequenceNumber(15) + .setSequenceNumber(99) .setGasUnitPrice(100) .setMaxGasAmount(3296766) .setExpirationTimestampSecs(3664390082) @@ -44,15 +44,15 @@ class TestAptosSigner { val result = AnySigner.sign(signingInput, CoinType.APTOS, Aptos.SigningOutput.parser()) assertEquals( Numeric.cleanHexPrefix(Numeric.toHexString(result.rawTxn.toByteArray())), - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000020" + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021" ) assertEquals( Numeric.cleanHexPrefix(Numeric.toHexString(result.authenticator.signature.toByteArray())), - "2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05" + "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01" ) assertEquals( Numeric.cleanHexPrefix(Numeric.toHexString(result.encoded.toByteArray())), - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c402ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05" + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01" ) } } diff --git a/src/Aptos/MoveTypes.h b/src/Aptos/MoveTypes.h index 7f77f9995ca..2a0b6728e3b 100644 --- a/src/Aptos/MoveTypes.h +++ b/src/Aptos/MoveTypes.h @@ -33,6 +33,10 @@ class ModuleId { Identifier mName; }; +inline ModuleId gAptosAccountModule{gAddressOne, "aptos_account"}; +inline ModuleId gAptosCoinModule{gAddressOne, "coin"}; +inline ModuleId gAptosTokenTransfersModule{gAddressThree, "token_transfers"}; + BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept; struct TypeTag; diff --git a/src/Aptos/Signer.cpp b/src/Aptos/Signer.cpp index e734c34edc4..d1c2684039d 100644 --- a/src/Aptos/Signer.cpp +++ b/src/Aptos/Signer.cpp @@ -33,23 +33,20 @@ std::pair, nlohmann::json> commonTransferPayload(const TPayloa TransactionPayload transferPayload(const Proto::SigningInput& input) { auto&& [args, argsJson] = commonTransferPayload(input.transfer()); - ModuleId module(gAddressOne, "coin"); - TransactionPayload payload = EntryFunction(module, "transfer", {gTransferTag}, args, argsJson); + TransactionPayload payload = EntryFunction(gAptosAccountModule, "transfer", {}, args, argsJson); return payload; } TransactionPayload createAccountPayload(const Proto::SigningInput& input) { - ModuleId module(gAddressOne, "aptos_account"); std::vector args; serializeToArgs(args, Address(input.create_account().auth_key())); nlohmann::json argsJson = nlohmann::json::array({input.create_account().auth_key()}); - TransactionPayload payload = EntryFunction(module, "create_account", {}, args, argsJson); + TransactionPayload payload = EntryFunction(gAptosAccountModule, "create_account", {}, args, argsJson); return payload; } TransactionPayload claimNftPayload(const Proto::ClaimNftMessage& msg) { std::vector args; - ModuleId module(gAddressThree, "token_transfers"); serializeToArgs(args, Address(msg.sender())); serializeToArgs(args, Address(msg.creator())); serializeToArgs(args, msg.collectionname()); @@ -65,13 +62,12 @@ TransactionPayload claimNftPayload(const Proto::ClaimNftMessage& msg) { std::to_string(msg.property_version()), }); // clang-format on - TransactionPayload payload = EntryFunction(module, "claim_script", {}, args, argsJson); + TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "claim_script", {}, args, argsJson); return payload; } TransactionPayload nftOfferPayload(const Proto::OfferNftMessage& msg) { std::vector args; - ModuleId module(gAddressThree, "token_transfers"); serializeToArgs(args, Address(msg.receiver())); serializeToArgs(args, Address(msg.creator())); serializeToArgs(args, msg.collectionname()); @@ -89,13 +85,12 @@ TransactionPayload nftOfferPayload(const Proto::OfferNftMessage& msg) { std::to_string(msg.amount()) }); // clang-format on - TransactionPayload payload = EntryFunction(module, "offer_script", {}, args, argsJson); + TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "offer_script", {}, args, argsJson); return payload; } TransactionPayload cancelNftOfferPayload(const Proto::CancelOfferNftMessage& msg) { std::vector args; - ModuleId module(gAddressThree, "token_transfers"); serializeToArgs(args, Address(msg.receiver())); serializeToArgs(args, Address(msg.creator())); serializeToArgs(args, msg.collectionname()); @@ -111,7 +106,7 @@ TransactionPayload cancelNftOfferPayload(const Proto::CancelOfferNftMessage& msg std::to_string(msg.property_version()), }); // clang-format on - TransactionPayload payload = EntryFunction(module, "cancel_offer_script", {}, args, argsJson); + TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "cancel_offer_script", {}, args, argsJson); return payload; } @@ -121,8 +116,7 @@ TransactionPayload tokenTransferPayload(const Proto::SigningInput& input) { auto& function = input.token_transfer().function(); TypeTag tokenTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), function.module(), function.name(), {})})}; - ModuleId module(gAddressOne, "coin"); - TransactionPayload payload = EntryFunction(module, "transfer", {tokenTransferTag}, args, argsJson); + TransactionPayload payload = EntryFunction(gAptosCoinModule, "transfer", {tokenTransferTag}, args, argsJson); return payload; } @@ -166,7 +160,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { case Proto::NftMessage::kClaimNft: return claimNftPayload(nftMessage.claim_nft()); case Proto::NftMessage::NFT_TRANSACTION_PAYLOAD_NOT_SET: - throw std::runtime_error("Nft message payload not set"); + throw std::runtime_error("Nft message payload not set"); } }; auto payloadFunctor = [&input, &nftPayloadFunctor]() { diff --git a/swift/Tests/Blockchains/AptosTests.swift b/swift/Tests/Blockchains/AptosTests.swift index d617c67db98..3c54581dafe 100644 --- a/swift/Tests/Blockchains/AptosTests.swift +++ b/swift/Tests/Blockchains/AptosTests.swift @@ -21,26 +21,26 @@ class AptosTests: XCTestCase { } func testSign() { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xbb3b3c33781c27e486afa2db854fb0a5c846d0967672feb2c6c3297a2b14e1ce?network=Devnet + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet let privateKeyData = Data(hexString: "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")! let transferMsg = AptosTransferMessage.with { $0.to = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" $0.amount = 1000 } let input = AptosSigningInput.with { - $0.chainID = 32 + $0.chainID = 33 $0.sender = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" $0.expirationTimestampSecs = 3664390082 $0.gasUnitPrice = 100 $0.maxGasAmount = 3296766 - $0.sequenceNumber = 15 + $0.sequenceNumber = 99 $0.transfer = transferMsg $0.privateKey = privateKeyData } let output: AptosSigningOutput = AnySigner.sign(input: input, coin: .aptos) - let expectedRawTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000020" - let expectedSignature = "2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05" - let expectedSignedTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c402ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05" + let expectedRawTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021" + let expectedSignature = "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01" + let expectedSignedTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01" XCTAssertEqual(output.rawTxn.hexString, expectedRawTx) XCTAssertEqual(output.authenticator.signature.hexString, expectedSignature) XCTAssertEqual(output.encoded.hexString, expectedSignedTx) diff --git a/tests/chains/Aptos/SignerTests.cpp b/tests/chains/Aptos/SignerTests.cpp index 3e0145d677b..bd796c0576b 100644 --- a/tests/chains/Aptos/SignerTests.cpp +++ b/tests/chains/Aptos/SignerTests.cpp @@ -16,47 +16,6 @@ namespace TW::Aptos::tests { -TEST(AptosSigner, DummyTxSign) { - Proto::SigningInput input; - input.set_sender("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); - input.set_sequence_number(1); - auto& tf = *input.mutable_transfer(); - tf.set_to("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); - tf.set_amount(1000); - input.set_max_gas_amount(1); - input.set_gas_unit_price(1); - input.set_expiration_timestamp_secs(1); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("7f2634c0e2414a621e96e39c41d09021700cee12ee43328ed094c5580cd0bd6f")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b010000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e80300000000000001000000000000000100000000000000010000000000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "9d3bd902bd358364c43fa65ece335dd4411527e72e1c6deb9148744eaa24e39b6bd74ff6b0195114243bdd2ee3a98511ff05883d9e79161b2b8f5029d883c309"); - ASSERT_EQ(hex(result.encoded()), "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b010000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000010000000000000001000000000000000100000000000000010020633e5c7e355bdd484706436ce1f06fdf280bd7c2229a7f9b6489684412c6967c409d3bd902bd358364c43fa65ece335dd4411527e72e1c6deb9148744eaa24e39b6bd74ff6b0195114243bdd2ee3a98511ff05883d9e79161b2b8f5029d883c309"); - nlohmann::json expectedJson = R"( - { - "sender": "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", - "sequence_number": "1", - "max_gas_amount": "1", - "gas_unit_price": "1", - "expiration_timestamp_secs": "1", - "payload": { - "type":"entry_function_payload", - "function": "0x1::coin::transfer", - "type_arguments":["0x1::aptos_coin::AptosCoin"], - "arguments": ["0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", "1000"] - }, - "signature": { - "type": "ed25519_signature", - "public_key": "0x633e5c7e355bdd484706436ce1f06fdf280bd7c2229a7f9b6489684412c6967c", - "signature": "0x9d3bd902bd358364c43fa65ece335dd4411527e72e1c6deb9148744eaa24e39b6bd74ff6b0195114243bdd2ee3a98511ff05883d9e79161b2b8f5029d883c309" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - TEST(AptosSigner, ClaimNftTxSign) { // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x60b51e15140ec0b7650334e948fb447ce3cb13ae63492260461ebfa9d02e85c4?network=testnet Proto::SigningInput input; @@ -203,23 +162,23 @@ TEST(AptosSigner, CancelNftOfferTxSign) { } TEST(AptosSigner, TxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xbb3b3c33781c27e486afa2db854fb0a5c846d0967672feb2c6c3297a2b14e1ce?network=Devnet + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet Proto::SigningInput input; input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(15); + input.set_sequence_number(99); auto& tf = *input.mutable_transfer(); tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); tf.set_amount(1000); input.set_max_gas_amount(3296766); input.set_gas_unit_price(100); input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(32); + input.set_chain_id(33); auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000020"); - ASSERT_EQ(hex(result.authenticator().signature()), "2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c402ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05"); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + ASSERT_EQ(hex(result.authenticator().signature()), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); nlohmann::json expectedJson = R"( { "expiration_timestamp_secs": "3664390082", @@ -227,15 +186,15 @@ TEST(AptosSigner, TxSign) { "max_gas_amount": "3296766", "payload": { "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], - "function": "0x1::coin::transfer", + "function": "0x1::aptos_account::transfer", "type": "entry_function_payload", - "type_arguments": ["0x1::aptos_coin::AptosCoin"] + "type_arguments": [] }, "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "15", + "sequence_number": "99", "signature": { "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", "type": "ed25519_signature" } } diff --git a/tests/chains/Aptos/TWAnySignerTests.cpp b/tests/chains/Aptos/TWAnySignerTests.cpp index e42890edf7b..e97b0977d9f 100644 --- a/tests/chains/Aptos/TWAnySignerTests.cpp +++ b/tests/chains/Aptos/TWAnySignerTests.cpp @@ -18,24 +18,24 @@ namespace TW::Aptos::tests { TEST(TWAnySignerAptos, TxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xbb3b3c33781c27e486afa2db854fb0a5c846d0967672feb2c6c3297a2b14e1ce?network=Devnet + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet Proto::SigningInput input; input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(15); + input.set_sequence_number(99); auto& tf = *input.mutable_transfer(); tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); tf.set_amount(1000); input.set_max_gas_amount(3296766); input.set_gas_unit_price(100); input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(32); + input.set_chain_id(33); auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); Proto::SigningOutput output; ANY_SIGN(input, TWCoinTypeAptos); - ASSERT_EQ(hex(output.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000020"); - ASSERT_EQ(hex(output.authenticator().signature()), "2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05"); - ASSERT_EQ(hex(output.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300f0000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c402ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05"); + ASSERT_EQ(hex(output.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + ASSERT_EQ(hex(output.authenticator().signature()), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); + ASSERT_EQ(hex(output.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); nlohmann::json expectedJson = R"( { "expiration_timestamp_secs": "3664390082", @@ -43,21 +43,21 @@ TEST(TWAnySignerAptos, TxSign) { "max_gas_amount": "3296766", "payload": { "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], - "function": "0x1::coin::transfer", + "function": "0x1::aptos_account::transfer", "type": "entry_function_payload", - "type_arguments": ["0x1::aptos_coin::AptosCoin"] + "type_arguments": [] }, "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "15", + "sequence_number": "99", "signature": { "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x2ac7acac0e597d04017b8d9ecad1ee7c2e07f3346957e507ac06508fe5c42c74892a347875d8d8826485a6e9b267bb7a0f24212be29c333c941c5db79c93ce05", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", "type": "ed25519_signature" } } )"_json; nlohmann::json parsedJson = nlohmann::json::parse(output.json()); - ASSERT_EQ(expectedJson, parsedJson); + assertJSONEqual(expectedJson, parsedJson); } } // namespace TW::Aptos::tests From cbcdb3ec856074c6998a842d30f9ef08c8b542b2 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 14 Oct 2022 10:18:08 +0200 Subject: [PATCH 019/426] [Aptos]: add register token capabilities(#2645) --- src/Aptos/MoveTypes.h | 1 + src/Aptos/Signer.cpp | 16 +++++++++-- src/Aptos/TransactionPayload.cpp | 2 +- src/proto/Aptos.proto | 32 +++++++++++++--------- tests/chains/Aptos/SignerTests.cpp | 43 ++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/Aptos/MoveTypes.h b/src/Aptos/MoveTypes.h index 2a0b6728e3b..c4360afcd83 100644 --- a/src/Aptos/MoveTypes.h +++ b/src/Aptos/MoveTypes.h @@ -35,6 +35,7 @@ class ModuleId { inline ModuleId gAptosAccountModule{gAddressOne, "aptos_account"}; inline ModuleId gAptosCoinModule{gAddressOne, "coin"}; +inline ModuleId gAptosManagedCoinsModule{gAddressOne, "managed_coin"}; inline ModuleId gAptosTokenTransfersModule{gAddressThree, "token_transfers"}; BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept; diff --git a/src/Aptos/Signer.cpp b/src/Aptos/Signer.cpp index d1c2684039d..9c15ea200af 100644 --- a/src/Aptos/Signer.cpp +++ b/src/Aptos/Signer.cpp @@ -120,6 +120,15 @@ TransactionPayload tokenTransferPayload(const Proto::SigningInput& input) { return payload; } +TransactionPayload registerTokenPayload(const Proto::SigningInput& input) { + + auto& function = input.register_token().function(); + TypeTag tokenRegisterTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), + function.module(), function.name(), {})})}; + TransactionPayload payload = EntryFunction(gAptosManagedCoinsModule, "register", {tokenRegisterTag}, {}); + return payload; +} + Proto::SigningOutput blindSign(const Proto::SigningInput& input) { auto output = Proto::SigningOutput(); BCS::Serializer serializer; @@ -174,9 +183,12 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { case Proto::SigningInput::kNftMessage: { return nftPayloadFunctor(input.nft_message()); } - case Proto::SigningInput::kCreateAccount: + case Proto::SigningInput::kCreateAccount: { return createAccountPayload(input); - + } + case Proto::SigningInput::kRegisterToken: { + return registerTokenPayload(input); + } case Proto::SigningInput::TRANSACTION_PAYLOAD_NOT_SET: throw std::runtime_error("Transaction payload should be set"); } diff --git a/src/Aptos/TransactionPayload.cpp b/src/Aptos/TransactionPayload.cpp index a37a6f7c60d..1e5b188e290 100644 --- a/src/Aptos/TransactionPayload.cpp +++ b/src/Aptos/TransactionPayload.cpp @@ -48,7 +48,7 @@ nlohmann::json EntryFunction::json() const noexcept { {"type", "entry_function_payload"}, {"function", mModule.shortString() + "::" + mFunction}, {"type_arguments", tyArgsJson}, - {"arguments", mJsonArgs} + {"arguments", mJsonArgs.empty() ? nlohmann::json::array() : mJsonArgs} }; // clang-format on return out; diff --git a/src/proto/Aptos.proto b/src/proto/Aptos.proto index 4d28f034868..1972c1ea349 100644 --- a/src/proto/Aptos.proto +++ b/src/proto/Aptos.proto @@ -37,6 +37,12 @@ message TokenTransferMessage { StructTag function = 3; } +// Necessary fields to process a ManagedTokensRegisterMessage +message ManagedTokensRegisterMessage { + // token function to register, e.g BTC: 0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC + StructTag function = 1; +} + // Necessary fields to process a CreateAccountMessage message CreateAccountMessage { // auth account address to create @@ -101,24 +107,26 @@ message SigningInput { string sender = 1; // Sequence number, incremented atomically for each tx present on the account, start at 0 (int64) int64 sequence_number = 2; - oneof transaction_payload { - TransferMessage transfer = 3; - TokenTransferMessage token_transfer = 4; - CreateAccountMessage create_account = 5; - NftMessage nft_message = 6; - } // Max gas amount that the user is willing to pay (uint64) - uint64 max_gas_amount = 7; + uint64 max_gas_amount = 3; // Gas unit price - queried through API (uint64) - uint64 gas_unit_price = 8; + uint64 gas_unit_price = 4; // Expiration timestamp for the transaction, can't be in the past (uint64) - uint64 expiration_timestamp_secs = 9; + uint64 expiration_timestamp_secs = 5; // Chain id 1 (mainnet) 32(devnet) (uint32 - casted in uint8_t later) - uint32 chain_id = 10; + uint32 chain_id = 6; // Private key to sign the transaction (bytes) - bytes private_key = 11; + bytes private_key = 7; // hex encoded function to sign, use it for smart contract approval (string) - string any_encoded = 12; + string any_encoded = 8; + + oneof transaction_payload { + TransferMessage transfer = 9; + TokenTransferMessage token_transfer = 10; + CreateAccountMessage create_account = 11; + NftMessage nft_message = 12; + ManagedTokensRegisterMessage register_token = 13; + } } // Information related to the signed transaction diff --git a/tests/chains/Aptos/SignerTests.cpp b/tests/chains/Aptos/SignerTests.cpp index bd796c0576b..c47c05bc3e5 100644 --- a/tests/chains/Aptos/SignerTests.cpp +++ b/tests/chains/Aptos/SignerTests.cpp @@ -288,6 +288,49 @@ TEST(AptosSigner, BlindSign) { assertJSONEqual(expectedJson, parsedJson); } +TEST(AptosSigner, TokenRegisterTxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xe591252daed785641bfbbcf72a5d17864568cf32e04c0cc9129f3a13834d0e8e?network=testnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(23); + auto& tf = *input.mutable_register_token(); + tf.mutable_function()->set_account_address("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379"); + tf.mutable_function()->set_module("move_coin"); + tf.mutable_function()->set_name("MoveCoin"); + input.set_max_gas_amount(2000000); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(2); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002"); + ASSERT_EQ(hex(result.authenticator().signature()), "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "2000000", + "payload": { + "arguments": [], + "function": "0x1::managed_coin::register", + "type": "entry_function_payload", + "type_arguments": ["0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin"] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "23", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xe230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + TEST(AptosSigner, TokenTxSign) { // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb5b383a5c7f99b2edb3bed9533f8169a89051b149d65876a82f4c0b9bf78a15b?network=Devnet Proto::SigningInput input; From 9e31eaf5ee338ac28de2fd823c9f948d81e67393 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 14 Oct 2022 11:02:12 +0200 Subject: [PATCH 020/426] [AnyAddress] Make AnyAddress work with arbitrary hrp (IBC chains) (#2643) --- .../app/blockchains/juno/TestJunoAddress.kt | 35 +++++++++++++ include/TrustWalletCore/TWAnyAddress.h | 28 +++++++++++ src/AnyAddress.cpp | 8 +-- src/AnyAddress.h | 4 +- src/Coin.cpp | 32 ++++++------ src/Coin.h | 10 ++-- src/Cosmos/Address.h | 5 ++ src/Cosmos/Entry.cpp | 14 ++++-- src/interface/TWAnyAddress.cpp | 23 +++++++++ swift/Tests/Addresses/JunoAddressTests.swift | 25 ++++++++++ tests/chains/Juno/TWAnyAddressTests.cpp | 50 +++++++++++++++++++ 11 files changed, 204 insertions(+), 30 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt create mode 100644 swift/Tests/Addresses/JunoAddressTests.swift create mode 100644 tests/chains/Juno/TWAnyAddressTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt new file mode 100644 index 00000000000..d7db293adf0 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 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.juno + +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert +import org.junit.Test +import wallet.core.jni.* + +class TestJunoAddress { + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAnyAddressValidation() { + val addr = "juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94" + val anyAddr = AnyAddress(addr, CoinType.COSMOS, "juno") + assert(AnyAddress.isValidBech32(anyAddr.description(), CoinType.COSMOS, "juno")) + assert(!AnyAddress.isValidBech32(anyAddr.description(), CoinType.BITCOIN, "juno")) + assert(!AnyAddress.isValid(anyAddr.description(), CoinType.BITCOIN)) + assert(!AnyAddress.isValid(anyAddr.description(), CoinType.COSMOS)) + } + + @Test + fun testAnyAddressFromPubkey() { + val pubKey = PublicKey("02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc".toHexByteArray(), PublicKeyType.SECP256K1) + val anyAddr = AnyAddress(pubKey, CoinType.COSMOS, "juno") + Assert.assertEquals(anyAddr.description(), "juno1cj2vfjec3c3luf9fx9vddnglhh9gawmncn4k5n"); + } +} diff --git a/include/TrustWalletCore/TWAnyAddress.h b/include/TrustWalletCore/TWAnyAddress.h index a815607989e..449d518ba2c 100644 --- a/include/TrustWalletCore/TWAnyAddress.h +++ b/include/TrustWalletCore/TWAnyAddress.h @@ -35,6 +35,15 @@ bool TWAnyAddressEqual(struct TWAnyAddress* _Nonnull lhs, struct TWAnyAddress* _ TW_EXPORT_STATIC_METHOD bool TWAnyAddressIsValid(TWString* _Nonnull string, enum TWCoinType coin); +/// Determines if the string is a valid Any address with the given hrp. +/// +/// \param string address to validate. +/// \param coin coin type of the address. +/// \param hrp explicit given hrp of the given address. +/// \return bool indicating if the address is valid. +TW_EXPORT_STATIC_METHOD +bool TWAnyAddressIsValidBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp); + /// Creates an address from a string representation and a coin type. Must be deleted with TWAnyAddressDelete after use. /// /// \param string address to create. @@ -43,6 +52,16 @@ bool TWAnyAddressIsValid(TWString* _Nonnull string, enum TWCoinType coin); TW_EXPORT_STATIC_METHOD struct TWAnyAddress* _Nullable TWAnyAddressCreateWithString(TWString* _Nonnull string, enum TWCoinType coin); +/// Creates an bech32 address from a string representation, a coin type and the given hrp. Must be deleted with TWAnyAddressDelete after use. +/// +/// \param string address to create. +/// \param coin coin type of the address. +/// \param hrp hrp of the address. +/// \return TWAnyAddress pointer or nullptr if address and coin are invalid. +TW_EXPORT_STATIC_METHOD +struct TWAnyAddress* _Nullable TWAnyAddressCreateBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp); + + /// Creates an address from a public key. /// /// \param publicKey derivates the address from the public key. @@ -51,6 +70,15 @@ struct TWAnyAddress* _Nullable TWAnyAddressCreateWithString(TWString* _Nonnull s TW_EXPORT_STATIC_METHOD struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKey(struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin); +/// Creates an bech32 address from a public key and a given hrp. +/// +/// \param publicKey derivates the address from the public key. +/// \param coin coin type of the address. +/// \param hrp hrp of the address. +/// \return TWAnyAddress pointer or nullptr if public key is invalid. +TW_EXPORT_STATIC_METHOD +struct TWAnyAddress* _Nonnull TWAnyAddressCreateBech32WithPublicKey(struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, TWString* _Nonnull hrp); + /// Deletes an address. /// /// \param address address to delete. diff --git a/src/AnyAddress.cpp b/src/AnyAddress.cpp index 504e309da1c..9bb265cc041 100644 --- a/src/AnyAddress.cpp +++ b/src/AnyAddress.cpp @@ -15,8 +15,8 @@ Data AnyAddress::getData() const { return TW::addressToData(coin, address); } -AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinType coin) { - auto normalized = TW::normalizeAddress(coin, address); +AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinType coin, const std::string& hrp) { + auto normalized = TW::normalizeAddress(coin, address, hrp); if (normalized.empty()) { return nullptr; } @@ -24,8 +24,8 @@ AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinTyp return new AnyAddress{.address = std::move(normalized), .coin = coin}; } -AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin) { - auto derivedAddress = TW::deriveAddress(coin, publicKey); +AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp) { + auto derivedAddress = TW::deriveAddress(coin, publicKey, TWDerivationDefault, hrp); return new AnyAddress{.address = std::move(derivedAddress), .coin = coin}; } diff --git a/src/AnyAddress.h b/src/AnyAddress.h index 78e8878a2fd..7bf773eaf07 100644 --- a/src/AnyAddress.h +++ b/src/AnyAddress.h @@ -22,8 +22,8 @@ class AnyAddress { enum TWCoinType coin; - static AnyAddress* createAddress(const std::string& address, enum TWCoinType coin); - static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin); + static AnyAddress* createAddress(const std::string& address, enum TWCoinType coin, const std::string& hrp = ""); + static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp = ""); Data getData() const; }; diff --git a/src/Coin.cpp b/src/Coin.cpp index b727c1488ed..efe10e47e56 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -25,6 +25,7 @@ #include "EOS/Entry.h" #include "Elrond/Entry.h" #include "Ethereum/Entry.h" +#include "Everscale/Entry.h" #include "FIO/Entry.h" #include "Filecoin/Entry.h" #include "Groestlcoin/Entry.h" @@ -42,7 +43,6 @@ #include "Oasis/Entry.h" #include "Ontology/Entry.h" #include "Polkadot/Entry.h" -#include "XRP/Entry.h" #include "Ronin/Entry.h" #include "Solana/Entry.h" #include "Stellar/Entry.h" @@ -52,9 +52,9 @@ #include "Tron/Entry.h" #include "VeChain/Entry.h" #include "Waves/Entry.h" +#include "XRP/Entry.h" #include "Zcash/Entry.h" #include "Zilliqa/Entry.h" -#include "Everscale/Entry.h" // end_of_coin_includes_marker_do_not_modify using namespace TW; @@ -165,7 +165,7 @@ const Derivation CoinInfo::derivationByName(TWDerivation nameIn) const { if (nameIn == TWDerivationDefault && derivation.size() > 0) { return derivation[0]; } - for (auto deriv: derivation) { + for (auto deriv : derivation) { if (deriv.name == nameIn) { return deriv; } @@ -173,10 +173,9 @@ const Derivation CoinInfo::derivationByName(TWDerivation nameIn) const { return Derivation(); } -bool TW::validateAddress(TWCoinType coin, const std::string& string) { +bool TW::validateAddress(TWCoinType coin, const std::string& string, const char* hrp) { auto p2pkh = TW::p2pkhPrefix(coin); auto p2sh = TW::p2shPrefix(coin); - const auto* hrp = stringForHRP(TW::hrp(coin)); // dispatch auto* dispatcher = coinDispatcher(coin); @@ -184,8 +183,14 @@ bool TW::validateAddress(TWCoinType coin, const std::string& string) { return dispatcher->validateAddress(coin, string, p2pkh, p2sh, hrp); } -std::string TW::normalizeAddress(TWCoinType coin, const std::string& address) { - if (!TW::validateAddress(coin, address)) { +bool TW::validateAddress(TWCoinType coin, const std::string& string) { + const auto* hrp = stringForHRP(TW::hrp(coin)); + return TW::validateAddress(coin, string, hrp); +} + +std::string TW::normalizeAddress(TWCoinType coin, const std::string& address, const std::string& hrp) { + const char* rawHrp = hrp.empty() ? stringForHRP(TW::hrp(coin)) : hrp.c_str(); + if (!TW::validateAddress(coin, address, rawHrp)) { // invalid address, not normalizing return ""; } @@ -205,18 +210,15 @@ std::string TW::deriveAddress(TWCoinType coin, const PrivateKey& privateKey, TWD return TW::deriveAddress(coin, privateKey.getPublicKey(keyType), derivation); } -std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey) { - return deriveAddress(coin, publicKey, TWDerivationDefault); -} - -std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation) { +std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const std::string& hrp) { auto p2pkh = TW::p2pkhPrefix(coin); - const auto* hrp = stringForHRP(TW::hrp(coin)); - + const char* hrpRaw = [&hrp, coin]() { + return hrp.empty() ? stringForHRP(TW::hrp(coin)) : hrp.c_str(); + }(); // dispatch auto* dispatcher = coinDispatcher(coin); assert(dispatcher != nullptr); - return dispatcher->deriveAddress(coin, derivation, publicKey, p2pkh, hrp); + return dispatcher->deriveAddress(coin, derivation, publicKey, p2pkh, hrpRaw); } Data TW::addressToData(TWCoinType coin, const std::string& address) { diff --git a/src/Coin.h b/src/Coin.h index e18cc7b88ea..aba8e7adef5 100644 --- a/src/Coin.h +++ b/src/Coin.h @@ -32,8 +32,11 @@ std::vector getCoinTypes(); /// Validates an address for a particular coin. bool validateAddress(TWCoinType coin, const std::string& address); +/// Validates an address for a particular coin. +bool validateAddress(TWCoinType coin, const std::string& address, const char* hrp); + /// Validates and normalizes an address for a particular coin. -std::string normalizeAddress(TWCoinType coin, const std::string& address); +std::string normalizeAddress(TWCoinType coin, const std::string& address, const std::string& hrp = ""); /// Returns the blockchain for a coin type. TWBlockchain blockchain(TWCoinType coin); @@ -74,11 +77,8 @@ std::string deriveAddress(TWCoinType coin, const PrivateKey& privateKey); /// Derives the address for a particular coin from the private key, with given derivation. std::string deriveAddress(TWCoinType coin, const PrivateKey& privateKey, TWDerivation derivation); -/// Derives the address for a particular coin from the public key. -std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey); - /// Derives the address for a particular coin from the public key, with given derivation. -std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation); +std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation = TWDerivationDefault, const std::string& hrp = ""); /// Returns the binary representation of a string address Data addressToData(TWCoinType coin, const std::string& address); diff --git a/src/Cosmos/Address.h b/src/Cosmos/Address.h index f13753db89a..cb629cfa607 100644 --- a/src/Cosmos/Address.h +++ b/src/Cosmos/Address.h @@ -40,6 +40,11 @@ class Address: public Bech32Address { return Bech32Address::isValid(addr, hrp); } + /// Determines whether a string makes a valid Bech32 address with the given hrp. + static bool isValid(const std::string& addr, const std::string& hrp) { + return Bech32Address::isValid(addr, hrp); + } + /// Creates an address object from the given string, if valid. Returns success. static bool decode(const std::string& addr, Address& obj_out) { return Bech32Address::decode(addr, obj_out, ""); diff --git a/src/Cosmos/Entry.cpp b/src/Cosmos/Entry.cpp index bb8ee4698d1..6cf9026fddd 100644 --- a/src/Cosmos/Entry.cpp +++ b/src/Cosmos/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -16,11 +16,17 @@ namespace TW::Cosmos { // 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, [[maybe_unused]] const char* hrp) const { - return Address::isValid(coin, address); +bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char* hrp) const { + if (hrpForString(hrp) != TWHRPUnknown) { + return Address::isValid(coin, address); + } + return Address::isValid(address, hrp); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, [[maybe_unused]] const char* hrp) const { +string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char* hrp) const { + if (!std::string(hrp).empty()) { + return Address(hrp, publicKey, coin).string(); + } return Address(coin, publicKey).string(); } diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index 57594be3847..6ad7a9fb9ce 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -21,6 +21,12 @@ bool TWAnyAddressIsValid(TWString* _Nonnull string, enum TWCoinType coin) { return TW::validateAddress(coin, address); } +bool TWAnyAddressIsValidBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp) { + const auto& address = *reinterpret_cast(string); + const auto& hrpStr = *reinterpret_cast(hrp); + return TW::validateAddress(coin, address, hrpStr.c_str()); +} + struct TWAnyAddress* _Nullable TWAnyAddressCreateWithString(TWString* _Nonnull string, enum TWCoinType coin) { const auto& address = *reinterpret_cast(string); @@ -31,11 +37,28 @@ struct TWAnyAddress* _Nullable TWAnyAddressCreateWithString(TWString* _Nonnull s return new TWAnyAddress{impl}; } +struct TWAnyAddress* _Nullable TWAnyAddressCreateBech32(TWString* _Nonnull string, + enum TWCoinType coin, TWString* _Nonnull hrp) { + const auto& address = *reinterpret_cast(string); + const auto& hrpStr = *reinterpret_cast(hrp); + auto *impl = TW::AnyAddress::createAddress(address, coin, hrpStr); + if (impl == nullptr) { + return nullptr; + } + return new TWAnyAddress{impl}; +} + struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKey( struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin) { return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin)}; } +struct TWAnyAddress* _Nonnull TWAnyAddressCreateBech32WithPublicKey( + struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, TWString* _Nonnull hrp) { + const auto& hrpStr = *reinterpret_cast(hrp); + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, hrpStr)}; +} + void TWAnyAddressDelete(struct TWAnyAddress* _Nonnull address) { delete address->impl; delete address; diff --git a/swift/Tests/Addresses/JunoAddressTests.swift b/swift/Tests/Addresses/JunoAddressTests.swift new file mode 100644 index 00000000000..02935c98c3a --- /dev/null +++ b/swift/Tests/Addresses/JunoAddressTests.swift @@ -0,0 +1,25 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class JunoAddressTests: XCTestCase { + func testAnyAddressValidation() { + let addr = AnyAddress(string: "juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94", coin: .cosmos, hrp: "juno")!; + XCTAssertTrue(AnyAddress.isValidBech32(string: addr.description, coin: .cosmos, hrp: "juno")); + XCTAssertFalse(AnyAddress.isValidBech32(string: addr.description, coin: .bitcoin, hrp: "juno")); + XCTAssertFalse(AnyAddress.isValid(string: addr.description, coin: .bitcoin)); + XCTAssertFalse(AnyAddress.isValid(string: addr.description, coin: .cosmos)); + } + + func testAnyAddressFromPubkey() { + let data = Data(hexString: "02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc")!; + let pubkey = PublicKey(data: data, type: .secp256k1)!; + let anyAddr = AnyAddress(publicKey: pubkey, coin: .cosmos, hrp: "juno"); + XCTAssertEqual(anyAddr.description, "juno1cj2vfjec3c3luf9fx9vddnglhh9gawmncn4k5n"); + } +} diff --git a/tests/chains/Juno/TWAnyAddressTests.cpp b/tests/chains/Juno/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..992176b9510 --- /dev/null +++ b/tests/chains/Juno/TWAnyAddressTests.cpp @@ -0,0 +1,50 @@ +// Copyright © 2017-2022 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 +#include "HexCoding.h" +#include "Hash.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TWJunoAnyAddress, IsValid) { + EXPECT_TRUE(TWAnyAddressIsValidBech32(STRING("juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94").get(), TWCoinTypeCosmos, STRING("juno").get())); + EXPECT_FALSE(TWAnyAddressIsValidBech32(STRING("juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94").get(), TWCoinTypeBitcoin, STRING("juno").get())); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94").get(), TWCoinTypeCosmos)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94").get(), TWCoinTypeBitcoin)); + +} + +TEST(TWJunoAnyAddress, createFromPubKeyJuno) { + const auto hrp = STRING("juno"); + const auto data = DATA("02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc"); + const auto pubkey = TWPublicKeyCreateWithData(data.get(), TWPublicKeyTypeSECP256k1); + const auto twAddress = TWAnyAddressCreateBech32WithPublicKey(pubkey, TWCoinTypeCosmos, hrp.get()); + auto twData = TWAnyAddressData(twAddress); + auto hexData = hex(*reinterpret_cast(twData)); + ASSERT_EQ(hexData, "c494c4cb388e23fe24a93158d6cd1fbdca8ebb73"); + ASSERT_EQ(hex(Bech32Address("juno", TW::Hash::HasherSha256ripemd, pubkey->impl).getKeyHash()), hexData); + auto address = TWAnyAddressDescription(twAddress); + EXPECT_EQ("juno1cj2vfjec3c3luf9fx9vddnglhh9gawmncn4k5n", *reinterpret_cast(address)); + TWStringDelete(address); + TWAnyAddressDelete(twAddress); + TWDataDelete(twData); + TWPublicKeyDelete(pubkey); +} + +TEST(TWJunoAnyAddress, createFromStringJuno) { + const auto junoAddress = STRING("juno1cj2vfjec3c3luf9fx9vddnglhh9gawmncn4k5n"); + const auto hrp = STRING("juno"); + const auto anyAddr = TWAnyAddressCreateBech32(junoAddress.get(), TWCoinTypeCosmos, hrp.get()); + const auto addrDescription = TWAnyAddressDescription(anyAddr); + ASSERT_TRUE(TWAnyAddressIsValidBech32(addrDescription, TWCoinTypeCosmos, hrp.get())); + TWStringDelete(addrDescription); + TWAnyAddressDelete(anyAddr); + +} From 8892ee9d3b909901783cb42761cf7b256b05d379 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 17 Oct 2022 15:52:09 +0200 Subject: [PATCH 021/426] [AnySigner] Add IBC (Juno) chain signing test (#2646) --- tests/chains/Juno/TWAnySignerTests.cpp | 56 +++++++++++++++++++++++ tests/chains/Osmosis/TWAnySignerTests.cpp | 1 - 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 tests/chains/Juno/TWAnySignerTests.cpp diff --git a/tests/chains/Juno/TWAnySignerTests.cpp b/tests/chains/Juno/TWAnySignerTests.cpp new file mode 100644 index 00000000000..f40103c1027 --- /dev/null +++ b/tests/chains/Juno/TWAnySignerTests.cpp @@ -0,0 +1,56 @@ +// Copyright © 2017-2022 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 "Cosmos/Address.h" +#include "HexCoding.h" +#include "proto/Cosmos.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Cosmos::tests { + +TEST(TWAnySignerJuno, Sign) { + auto privateKey = parse_hex("a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433"); + Proto::SigningInput input; + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(376606); + input.set_chain_id("juno-1"); + input.set_memo(""); + input.set_sequence(0); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address("juno1mry47pkga5tdswtluy0m8teslpalkdq0gnn4mf"); + message.set_to_address("juno1mry47pkga5tdswtluy0m8teslpalkdq0gnn4mf"); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("ujuno"); + amountOfTx->set_amount("10000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(80000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("ujuno"); + amountOfFee->set_amount("1000"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeCosmos); + + // https://www.mintscan.io/juno/txs/3DCE6AAF19657BCF11D44FD6BE124D57B44E04CA34851DE0ECCE619F70ECC46F + auto expectedJson = R"( + { + "mode": "BROADCAST_MODE_BLOCK", + "tx_bytes": "Co0BCooBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmoKK2p1bm8xbXJ5NDdwa2dhNXRkc3d0bHV5MG04dGVzbHBhbGtkcTBnbm40bWYSK2p1bm8xbXJ5NDdwa2dhNXRkc3d0bHV5MG04dGVzbHBhbGtkcTBnbm40bWYaDgoFdWp1bm8SBTEwMDAwEmUKTgpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQLL/bXkcokzIilOYM8Ig9Q99DHhCJ0p7LRHqebVUEWq5RIECgIIARITCg0KBXVqdW5vEgQxMDAwEIDxBBpABrA2SUNtur1XqAIzNjM4UYtFylKARkfMd2YJUi11qqMkX0rZfmHrELL+QqjERn0o3vsR231fmPGJe4P0Isjwjw==" + })"; + assertJSONEqual(output.serialized(), expectedJson); + EXPECT_EQ(hex(output.signature()), "06b03649436dbabd57a80233363338518b45ca52804647cc776609522d75aaa3245f4ad97e61eb10b2fe42a8c4467d28defb11db7d5f98f1897b83f422c8f08f"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Osmosis/TWAnySignerTests.cpp b/tests/chains/Osmosis/TWAnySignerTests.cpp index 95f230551a8..64aa657c25c 100644 --- a/tests/chains/Osmosis/TWAnySignerTests.cpp +++ b/tests/chains/Osmosis/TWAnySignerTests.cpp @@ -47,7 +47,6 @@ TEST(TWAnySignerOsmosis, Sign) { ANY_SIGN(input, TWCoinTypeOsmosis); // https://www.mintscan.io/osmosis/txs/81B4F01BDE72AF7FF4536E5D7E66EB218E9FC9ACAA7C5EB5DB237DD0595D5F5F - // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Co0B...rYVj", "mode": "BROADCAST_MODE_BLOCK"}' https://lcd-osmosis.keplr.app/cosmos/tx/v1beta1/txs assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"Co0BCooBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmoKK29zbW8xbWt5Njljbjhla3R3eTA4NDV2ZWM5dXBzZHBoa3R4dDBlbjk3ZjUSK29zbW8xOHMwaGRuc2xsZ2NjbHdldTlheW13NG5na3RyMmswcmt2bjdqbW4aDgoFdW9zbW8SBTk5ODAwEmQKTgpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQLs71zkN6MCxn+VRo3ksx826RH0Z9fmpStBweE+HVY2SRIECgIIARISCgwKBXVvc21vEgMyMDAQwJoMGkAMY//Md5GRUR4lVZhk558hFS3kii9QZYoYKfg4+ac/xgNeyoiEweVDhcmEvlH1orVwjLUOnYs4ly2a/yIurYVj\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); EXPECT_EQ(hex(output.signature()), "0c63ffcc779191511e25559864e79f21152de48a2f50658a1829f838f9a73fc6035eca8884c1e54385c984be51f5a2b5708cb50e9d8b38972d9aff222ead8563"); EXPECT_EQ(output.json(), ""); From f03a6be12bd4fff33d4d4bb9556acf8a61653e89 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 18 Oct 2022 08:56:03 +0200 Subject: [PATCH 022/426] feat(aptos): update aptos registry.json with mainnet values (#2658) --- registry.json | 8 ++++---- tests/chains/Aptos/TWCoinTypeTests.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/registry.json b/registry.json index d77105beebe..4de03d0c13e 100644 --- a/registry.json +++ b/registry.json @@ -419,14 +419,14 @@ "url": "https://explorer.aptoslabs.com", "txPath": "/txn/", "accountPath": "/account/", - "sampleTx": "91424546", - "sampleAccount": "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108" + "sampleTx": "0xedc88058e27f6c065fd6607e262cb2a83a65f74301df90c61923014c59f9d465", + "sampleAccount": "0x60ad80e8cdadb81399e8a738014bc9ec865cef842f7c2cf7d84fbf7e40d065" }, "info": { "url": "https://aptoslabs.com/", "source": "https://github.com/aptos-labs/aptos-core", - "rpc": "https://fullnode.devnet.aptoslabs.com", - "documentation": "https://fullnode.devnet.aptoslabs.com/v1/spec#/" + "rpc": "https://fullnode.mainnet.aptoslabs.com/v1", + "documentation": "https://fullnode.mainnet.aptoslabs.com/v1/spec#/" } }, { diff --git a/tests/chains/Aptos/TWCoinTypeTests.cpp b/tests/chains/Aptos/TWCoinTypeTests.cpp index af10a478945..25f89208b5d 100644 --- a/tests/chains/Aptos/TWCoinTypeTests.cpp +++ b/tests/chains/Aptos/TWCoinTypeTests.cpp @@ -18,9 +18,9 @@ TEST(TWAptosCoinType, TWCoinType) { const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); - const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("91424546")); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xedc88058e27f6c065fd6607e262cb2a83a65f74301df90c61923014c59f9d465")); const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); - const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108")); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x60ad80e8cdadb81399e8a738014bc9ec865cef842f7c2cf7d84fbf7e40d065")); const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); assertStringsEqual(id, "aptos"); @@ -30,6 +30,6 @@ TEST(TWAptosCoinType, TWCoinType) { ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainAptos); ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); - assertStringsEqual(txUrl, "https://explorer.aptoslabs.com/txn/91424546"); - assertStringsEqual(accUrl, "https://explorer.aptoslabs.com/account/0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108"); + assertStringsEqual(txUrl, "https://explorer.aptoslabs.com/txn/0xedc88058e27f6c065fd6607e262cb2a83a65f74301df90c61923014c59f9d465"); + assertStringsEqual(accUrl, "https://explorer.aptoslabs.com/account/0x60ad80e8cdadb81399e8a738014bc9ec865cef842f7c2cf7d84fbf7e40d065"); } From 49877b1792b78851979ed3fa90fd8d400729464a Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Tue, 18 Oct 2022 12:21:17 +0100 Subject: [PATCH 023/426] [Fix/Codegen]: make Entry class final by default, fix include path (#2662) --- codegen/lib/templates/newcoin/Address.h.erb | 2 +- codegen/lib/templates/newcoin/Entry.h.erb | 2 +- codegen/lib/templates/newcoin/Signer.h.erb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen/lib/templates/newcoin/Address.h.erb b/codegen/lib/templates/newcoin/Address.h.erb index 9c6f60df4f6..74997f6330c 100644 --- a/codegen/lib/templates/newcoin/Address.h.erb +++ b/codegen/lib/templates/newcoin/Address.h.erb @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/codegen/lib/templates/newcoin/Entry.h.erb b/codegen/lib/templates/newcoin/Entry.h.erb index 1b58f5f78cd..fa115a9a30b 100644 --- a/codegen/lib/templates/newcoin/Entry.h.erb +++ b/codegen/lib/templates/newcoin/Entry.h.erb @@ -12,7 +12,7 @@ namespace TW::<%= format_name(coin) %> { /// Entry point for implementation of <%= format_name(coin) %> 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 { +class Entry final : public CoinEntry { public: 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; diff --git a/codegen/lib/templates/newcoin/Signer.h.erb b/codegen/lib/templates/newcoin/Signer.h.erb index 1261f9c6149..6be63a95699 100644 --- a/codegen/lib/templates/newcoin/Signer.h.erb +++ b/codegen/lib/templates/newcoin/Signer.h.erb @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/<%= name %>.pb.h" From 55993e9b9418d5af30e1138455076343445e5071 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 21 Oct 2022 18:02:41 +0200 Subject: [PATCH 024/426] [PrivateKey]: add new C interface to get public key with cointype (#2661) --- .../core/app/utils/TestPrivateKey.kt | 13 +++++++---- include/TrustWalletCore/TWCoinType.h | 5 +++- include/TrustWalletCore/TWPrivateKey.h | 17 ++++++++++++++ src/interface/TWPrivateKey.cpp | 23 +++++++++++++------ .../BinanceSmartChain/TWAnyAddressTests.cpp | 1 + tests/chains/Bitcoin/MessageSignerTests.cpp | 1 + tests/chains/Evmos/TWAnyAddressTests.cpp | 1 + tests/chains/Juno/TWAnyAddressTests.cpp | 3 +++ .../chains/NativeEvmos/TWAnyAddressTests.cpp | 1 + tests/interface/TWPrivateKeyTests.cpp | 17 ++++++++++++++ 10 files changed, 69 insertions(+), 13 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt index abdbc8050ab..4b6b4908db8 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt @@ -3,11 +3,7 @@ package com.trustwallet.core.app.utils import com.trustwallet.core.app.utils.toHexBytes import org.junit.Assert.* import org.junit.Test -import wallet.core.jni.Curve -import wallet.core.jni.Hash -import wallet.core.jni.PrivateKey -import wallet.core.jni.PublicKey -import wallet.core.jni.PublicKeyType +import wallet.core.jni.* class TestPrivateKey { private val validPrivateKeyData = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5".toHexBytes() @@ -80,6 +76,13 @@ class TestPrivateKey { assertEquals(derivedData?.toHex(), "0xef2cf705af8714b35c0855030f358f2bee356ff3579cea2607b2025d80133c3a") } + @Test + fun testGetPublicKeyCoinType() { + val privateKeyData = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5".toHexBytes() + val privateKey = PrivateKey(privateKeyData) + assertEquals(privateKey.getPublicKey(CoinType.ETHEREUM).data().toHex(), "0x0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91"); + } + @Test fun testGetSharedKeyWycherproof() { val privateKeyData = "f4b7ff7cccc98813a69fae3df222bfe3f4e28f764bf91b4a10d8096ce446b254".toHexBytes() diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 52e5f2d50f5..4ed7c86a3da 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -11,13 +11,16 @@ #include "TWCurve.h" #include "TWHDVersion.h" #include "TWHRP.h" -#include "TWPrivateKey.h" #include "TWPurpose.h" #include "TWString.h" #include "TWDerivation.h" +#include "TWPublicKeyType.h" TW_EXTERN_C_BEGIN +/// Represents a private key. +struct TWPrivateKey; + /// Coin type for Level 2 of BIP44. /// /// \see https://github.com/satoshilabs/slips/blob/master/slip-0044.md diff --git a/include/TrustWalletCore/TWPrivateKey.h b/include/TrustWalletCore/TWPrivateKey.h index ee4cf3fc7e2..d84a988d79a 100644 --- a/include/TrustWalletCore/TWPrivateKey.h +++ b/include/TrustWalletCore/TWPrivateKey.h @@ -10,6 +10,7 @@ #include "TWCurve.h" #include "TWData.h" #include "TWPublicKey.h" +#include "TWCoinType.h" TW_EXTERN_C_BEGIN @@ -63,6 +64,22 @@ bool TWPrivateKeyIsValid(TWData* _Nonnull data, enum TWCurve curve); TW_EXPORT_PROPERTY TWData* _Nonnull TWPrivateKeyData(struct TWPrivateKey* _Nonnull pk); +/// Returns the public key associated with the given coinType and privateKey +/// +/// \param pk Non-null pointer to the private key +/// \param coinType coinType of the given private key +/// \return Non-null pointer to the corresponding public key +TW_EXPORT_METHOD +struct TWPublicKey* _Nonnull TWPrivateKeyGetPublicKey(struct TWPrivateKey* _Nonnull pk, enum TWCoinType coinType); + +/// Returns the public key associated with the given pubkeyType and privateKey +/// +/// \param pk Non-null pointer to the private key +/// \param pubkeyType pubkeyType of the given private key +/// \return Non-null pointer to the corresponding public key +TW_EXPORT_METHOD +struct TWPublicKey* _Nonnull TWPrivateKeyGetPublicKeyByType(struct TWPrivateKey* _Nonnull pk, enum TWPublicKeyType pubkeyType); + /// Returns the Secp256k1 public key associated with the given private key /// /// \param pk Non-null pointer to the private key diff --git a/src/interface/TWPrivateKey.cpp b/src/interface/TWPrivateKey.cpp index ab91378d9ca..08e9eb49490 100644 --- a/src/interface/TWPrivateKey.cpp +++ b/src/interface/TWPrivateKey.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -62,31 +63,31 @@ TWData *TWPrivateKeyData(struct TWPrivateKey *_Nonnull pk) { } struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyNist256p1(struct TWPrivateKey *_Nonnull pk) { - return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeNIST256p1) }; + return TWPrivateKeyGetPublicKeyByType(pk, TWPublicKeyTypeNIST256p1); } struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeySecp256k1(struct TWPrivateKey *_Nonnull pk, bool compressed) { if (compressed) { - return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeSECP256k1) }; + return TWPrivateKeyGetPublicKeyByType(pk, TWPublicKeyTypeSECP256k1); } else { - return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeSECP256k1Extended) }; + return TWPrivateKeyGetPublicKeyByType(pk, TWPublicKeyTypeSECP256k1Extended); } } struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519(struct TWPrivateKey *_Nonnull pk) { - return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeED25519) }; + return TWPrivateKeyGetPublicKeyByType(pk, TWPublicKeyTypeED25519); } struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519Blake2b(struct TWPrivateKey *_Nonnull pk) { - return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeED25519Blake2b) }; + return TWPrivateKeyGetPublicKeyByType(pk, TWPublicKeyTypeED25519Blake2b); } struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519Cardano(struct TWPrivateKey *_Nonnull pk) { - return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeED25519Cardano) }; + return TWPrivateKeyGetPublicKeyByType(pk, TWPublicKeyTypeED25519Cardano); } struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyCurve25519(struct TWPrivateKey *_Nonnull pk) { - return new TWPublicKey{pk->impl.getPublicKey(TWPublicKeyTypeCURVE25519)}; + return TWPrivateKeyGetPublicKeyByType(pk, TWPublicKeyTypeCURVE25519); } TWData *_Nullable TWPrivateKeyGetSharedKey(const struct TWPrivateKey *_Nonnull pk, const struct TWPublicKey *_Nonnull publicKey, enum TWCurve curve) { @@ -128,3 +129,11 @@ TWData *TWPrivateKeySignZilliqaSchnorr(struct TWPrivateKey *_Nonnull pk, TWData return TWDataCreateWithBytes(result.data(), result.size()); } } + +struct TWPublicKey* TWPrivateKeyGetPublicKey(struct TWPrivateKey* pk, enum TWCoinType coinType) { + return TWPrivateKeyGetPublicKeyByType(pk, TWCoinTypePublicKeyType(coinType)); +} + +struct TWPublicKey* TWPrivateKeyGetPublicKeyByType(struct TWPrivateKey* pk, enum TWPublicKeyType pubkeyType) { + return new TWPublicKey{ pk->impl.getPublicKey(pubkeyType) }; +} diff --git a/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp b/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp index f7769d5c198..747f722a61e 100644 --- a/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp +++ b/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #include +#include #include "HexCoding.h" #include "TestUtilities.h" diff --git a/tests/chains/Bitcoin/MessageSignerTests.cpp b/tests/chains/Bitcoin/MessageSignerTests.cpp index 5ec7d6d361a..df617721300 100644 --- a/tests/chains/Bitcoin/MessageSignerTests.cpp +++ b/tests/chains/Bitcoin/MessageSignerTests.cpp @@ -13,6 +13,7 @@ #include "Base64.h" #include "Coin.h" #include "Data.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/Evmos/TWAnyAddressTests.cpp b/tests/chains/Evmos/TWAnyAddressTests.cpp index de3468d4cce..63b9462654a 100644 --- a/tests/chains/Evmos/TWAnyAddressTests.cpp +++ b/tests/chains/Evmos/TWAnyAddressTests.cpp @@ -8,6 +8,7 @@ #include #include +#include #include diff --git a/tests/chains/Juno/TWAnyAddressTests.cpp b/tests/chains/Juno/TWAnyAddressTests.cpp index 992176b9510..2eddab2c3c7 100644 --- a/tests/chains/Juno/TWAnyAddressTests.cpp +++ b/tests/chains/Juno/TWAnyAddressTests.cpp @@ -5,6 +5,9 @@ // file LICENSE at the root of the source code distribution tree. #include +#include +#include "PublicKey.h" +#include "Bech32Address.h" #include "HexCoding.h" #include "Hash.h" diff --git a/tests/chains/NativeEvmos/TWAnyAddressTests.cpp b/tests/chains/NativeEvmos/TWAnyAddressTests.cpp index ed2a02cd50f..cb76b9cc310 100644 --- a/tests/chains/NativeEvmos/TWAnyAddressTests.cpp +++ b/tests/chains/NativeEvmos/TWAnyAddressTests.cpp @@ -7,6 +7,7 @@ #include "TestUtilities.h" #include +#include #include #include diff --git a/tests/interface/TWPrivateKeyTests.cpp b/tests/interface/TWPrivateKeyTests.cpp index 2ce9d6be10c..fd9787e2732 100644 --- a/tests/interface/TWPrivateKeyTests.cpp +++ b/tests/interface/TWPrivateKeyTests.cpp @@ -83,6 +83,23 @@ TEST(TWPrivateKeyTests, PublicKey) { const auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyCurve25519(privateKey.get())); ASSERT_EQ(TW::hex(publicKey.get()->impl.bytes), "686cfce9108566dd43fc6aa75e31f9a9f319c9e9c04d6ad0a52505b86bc17c3a"); } + { + const auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeEthereum)); + ASSERT_EQ(TW::hex(publicKey.get()->impl.bytes), "0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91"); + + auto pubkeyType = TWCoinTypePublicKeyType(TWCoinTypeEthereum); + const auto publicKeyByType = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyByType(privateKey.get(), pubkeyType)); + + ASSERT_EQ(TW::hex(publicKey.get()->impl.bytes), TW::hex(publicKeyByType.get()->impl.bytes)); + } + { + const auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeNEO)); + ASSERT_EQ(TW::hex(publicKey.get()->impl.bytes), "026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab"); + } + { + const auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeWaves)); + ASSERT_EQ(TW::hex(publicKey.get()->impl.bytes), "686cfce9108566dd43fc6aa75e31f9a9f319c9e9c04d6ad0a52505b86bc17c3a"); + } } TEST(TWPrivateKeyTests, GetSharedKey) { From 9a5a44361eb4fb5c556a8ca8665b541751abf83f Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 25 Oct 2022 06:33:16 +0200 Subject: [PATCH 025/426] [Polkadot]: Runtime network id for polkadot (#2663) --- .../blockchains/kusama/TestKusamaSigner.kt | 2 +- .../polkadot/TestPolkadotSigner.kt | 6 +- codegen/lib/templates/CoinInfoData.cpp.erb | 2 + codegen/lib/templates/newcoin/Entry.cpp.erb | 2 +- include/TrustWalletCore/TWAnyAddress.h | 27 ++++++++ include/TrustWalletCore/TWCoinType.h | 7 ++ registry.json | 2 + src/Aeternity/Entry.cpp | 2 +- src/Aeternity/Entry.h | 2 +- src/Aion/Entry.cpp | 2 +- src/Aion/Entry.h | 2 +- src/Algorand/Entry.cpp | 2 +- src/Algorand/Entry.h | 2 +- src/AnyAddress.cpp | 12 +++- src/AnyAddress.h | 4 +- src/Aptos/Entry.cpp | 2 +- src/Aptos/Entry.h | 2 +- src/Binance/Entry.cpp | 2 +- src/Binance/Entry.h | 2 +- src/Bitcoin/Entry.cpp | 21 +++--- src/Bitcoin/Entry.h | 3 +- src/Cardano/Entry.cpp | 2 +- src/Cardano/Entry.h | 2 +- src/Coin.cpp | 64 +++++++++++++++---- src/Coin.h | 11 +++- src/CoinEntry.h | 16 ++++- src/Cosmos/Entry.cpp | 11 ++-- src/Cosmos/Entry.h | 2 +- src/Decred/Entry.cpp | 2 +- src/Decred/Entry.h | 10 +-- src/EOS/Entry.cpp | 2 +- src/EOS/Entry.h | 6 +- src/Elrond/Entry.cpp | 2 +- src/Elrond/Entry.h | 14 ++-- src/Ethereum/Entry.cpp | 2 +- src/Ethereum/Entry.h | 2 +- src/Everscale/Entry.cpp | 2 +- src/Everscale/Entry.h | 2 +- src/FIO/Entry.cpp | 2 +- src/FIO/Entry.h | 2 +- src/Filecoin/Entry.cpp | 3 +- src/Filecoin/Entry.h | 3 +- src/Groestlcoin/Entry.cpp | 7 +- src/Groestlcoin/Entry.h | 2 +- src/Harmony/Entry.cpp | 2 +- src/Harmony/Entry.h | 2 +- src/Icon/Address.cpp | 8 +-- src/Icon/Entry.cpp | 2 +- src/Icon/Entry.h | 2 +- src/IoTeX/Entry.cpp | 2 +- src/IoTeX/Entry.h | 2 +- src/Kusama/Entry.cpp | 2 +- src/Kusama/Entry.h | 2 +- src/NEAR/Entry.cpp | 2 +- src/NEAR/Entry.h | 2 +- src/NEO/Entry.cpp | 2 +- src/NEO/Entry.h | 2 +- src/NULS/Entry.cpp | 2 +- src/NULS/Entry.h | 2 +- src/Nano/Entry.cpp | 2 +- src/Nano/Entry.h | 2 +- src/Nebulas/Entry.cpp | 2 +- src/Nebulas/Entry.h | 2 +- src/Nervos/Entry.cpp | 6 +- src/Nervos/Entry.h | 3 +- src/Nimiq/Entry.cpp | 2 +- src/Nimiq/Entry.h | 2 +- src/Oasis/Entry.cpp | 2 +- src/Oasis/Entry.h | 2 +- src/Ontology/Entry.cpp | 2 +- src/Ontology/Entry.h | 2 +- src/Polkadot/Address.h | 7 +- src/Polkadot/Entry.cpp | 12 +++- src/Polkadot/Entry.h | 9 +-- src/Polkadot/SS58Address.cpp | 2 +- src/Polkadot/SS58Address.h | 2 +- src/Ronin/Entry.cpp | 2 +- src/Ronin/Entry.h | 2 +- src/Solana/Entry.cpp | 2 +- src/Solana/Entry.h | 2 +- src/Stellar/Entry.cpp | 2 +- src/Stellar/Entry.h | 2 +- src/Tezos/Entry.cpp | 2 +- src/Tezos/Entry.h | 2 +- src/Tron/Entry.cpp | 2 +- src/Tron/Entry.h | 2 +- src/Waves/Entry.cpp | 2 +- src/Waves/Entry.h | 2 +- src/XRP/Entry.cpp | 2 +- src/XRP/Entry.h | 2 +- src/Zcash/Entry.cpp | 2 +- src/Zcash/Entry.h | 2 +- src/Zilliqa/Entry.cpp | 2 +- src/Zilliqa/Entry.h | 2 +- src/interface/TWAnyAddress.cpp | 21 +++++- src/interface/TWCoinType.cpp | 4 ++ src/proto/Polkadot.proto | 8 +-- swift/Tests/Blockchains/KusamaTests.swift | 2 +- swift/Tests/Blockchains/PolkadotTests.swift | 10 +-- tests/chains/Acala/TWAnyAddressTests.cpp | 49 ++++++++++++++ tests/chains/Juno/TWAnyAddressTests.cpp | 2 + tests/chains/Kusama/SignerTests.cpp | 3 +- tests/chains/Kusama/TWAnySignerTests.cpp | 2 +- tests/chains/Polkadot/SignerTests.cpp | 25 ++++---- 104 files changed, 367 insertions(+), 171 deletions(-) create mode 100644 tests/chains/Acala/TWAnyAddressTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaSigner.kt index 738ff2a4c1f..388a38bd150 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaSigner.kt @@ -36,7 +36,7 @@ class TestKusamaSigner { blockHash = hash nonce = 1 specVersion = 2019 - network = Polkadot.Network.KUSAMA + network = KUSAMA.ss58Prefix() transactionVersion = 2 privateKey = key balanceCall = Polkadot.Balance.newBuilder().apply { diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt index 2e5a09f2885..9da312ca3ea 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt @@ -39,7 +39,7 @@ class TestPolkadotSigner { blockHash = genesisHashStr nonce = 0 specVersion = 17 - network = Polkadot.Network.POLKADOT + network = POLKADOT.ss58Prefix() transactionVersion = 3 privateKey = key stakingCall = Polkadot.Staking.newBuilder().apply { @@ -69,7 +69,7 @@ class TestPolkadotSigner { blockHash = genesisHashStr nonce = 4 specVersion = 30 - network = Polkadot.Network.POLKADOT + network = POLKADOT.ss58Prefix() transactionVersion = 7 privateKey = iOSTestKey stakingCall = Polkadot.Staking.newBuilder().apply { @@ -96,7 +96,7 @@ class TestPolkadotSigner { blockHash = "0x35ba668bb19453e8da6334cadcef2a27c8d4141bfc8b49e78e853c3d73e1ecd0".toHexBytesInByteString() nonce = 6 specVersion = 9200 - network = Polkadot.Network.POLKADOT + network = POLKADOT.ss58Prefix() transactionVersion = 12 privateKey = "298fcced2b497ed48367261d8340f647b3fca2d9415d57c2e3c5ef90482a2266".toHexBytesInByteString() era = Polkadot.Era.newBuilder().apply { diff --git a/codegen/lib/templates/CoinInfoData.cpp.erb b/codegen/lib/templates/CoinInfoData.cpp.erb index 44dd8acd044..78aed10c204 100644 --- a/codegen/lib/templates/CoinInfoData.cpp.erb +++ b/codegen/lib/templates/CoinInfoData.cpp.erb @@ -36,6 +36,7 @@ static const CoinInfo defaultsForMissing = { "", "", 0, + 0 }; /// Get coin from map, if missing returns defaults (not to have contains-check in each accessor method) @@ -73,6 +74,7 @@ const CoinInfo getCoinInfo(TWCoinType coin) { "<%= explorer_tx_url(coin) %>", "<%= explorer_account_url(coin) %>", <% if coin['slip44'].nil? -%><%= coin['coinId'] %><% else -%><%= coin['slip44'] %><% end -%>, + <% if coin['ss58Prefix'].nil? -%>0<% else -%><%= coin['ss58Prefix'] %><% end -%>, }; <% end -%> default: diff --git a/codegen/lib/templates/newcoin/Entry.cpp.erb b/codegen/lib/templates/newcoin/Entry.cpp.erb index d0585ed64b9..a29ceb91951 100644 --- a/codegen/lib/templates/newcoin/Entry.cpp.erb +++ b/codegen/lib/templates/newcoin/Entry.cpp.erb @@ -13,7 +13,7 @@ namespace TW::<%= format_name(coin) %> { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress(TWCoinType coin, [[maybe_unused]] const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/include/TrustWalletCore/TWAnyAddress.h b/include/TrustWalletCore/TWAnyAddress.h index 449d518ba2c..86dcdc51bd9 100644 --- a/include/TrustWalletCore/TWAnyAddress.h +++ b/include/TrustWalletCore/TWAnyAddress.h @@ -44,6 +44,15 @@ bool TWAnyAddressIsValid(TWString* _Nonnull string, enum TWCoinType coin); TW_EXPORT_STATIC_METHOD bool TWAnyAddressIsValidBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp); +/// Determines if the string is a valid Any address with the given SS58 network prefix. +/// +/// \param string address to validate. +/// \param coin coin type of the address. +/// \param ss58Prefix ss58Prefix of the given address. +/// \return bool indicating if the address is valid. +TW_EXPORT_STATIC_METHOD +bool TWAnyAddressIsValidSS58(TWString* _Nonnull string, enum TWCoinType coin, uint32_t ss58Prefix); + /// Creates an address from a string representation and a coin type. Must be deleted with TWAnyAddressDelete after use. /// /// \param string address to create. @@ -61,6 +70,15 @@ struct TWAnyAddress* _Nullable TWAnyAddressCreateWithString(TWString* _Nonnull s TW_EXPORT_STATIC_METHOD struct TWAnyAddress* _Nullable TWAnyAddressCreateBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp); +/// Creates an SS58 address from a string representation, a coin type and the given ss58Prefix. Must be deleted with TWAnyAddressDelete after use. +/// +/// \param string address to create. +/// \param coin coin type of the address. +/// \param ss58Prefix ss58Prefix of the SS58 address. +/// \return TWAnyAddress pointer or nullptr if address and coin are invalid. +TW_EXPORT_STATIC_METHOD +struct TWAnyAddress* _Nullable TWAnyAddressCreateSS58(TWString* _Nonnull string, enum TWCoinType coin, uint32_t ss58Prefix); + /// Creates an address from a public key. /// @@ -79,6 +97,15 @@ struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKey(struct TWPublicKey TW_EXPORT_STATIC_METHOD struct TWAnyAddress* _Nonnull TWAnyAddressCreateBech32WithPublicKey(struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, TWString* _Nonnull hrp); +/// Creates an SS58 address from a public key and a given ss58Prefix. +/// +/// \param publicKey derivates the address from the public key. +/// \param coin coin type of the address. +/// \param ss58Prefix ss58Prefix of the SS58 address. +/// \return TWAnyAddress pointer or nullptr if public key is invalid. +TW_EXPORT_STATIC_METHOD +struct TWAnyAddress* _Nonnull TWAnyAddressCreateSS58WithPublicKey(struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, uint32_t ss58Prefix); + /// Deletes an address. /// /// \param address address to delete. diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 4ed7c86a3da..4c009702001 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -242,6 +242,13 @@ TWString* _Nonnull TWCoinTypeChainId(enum TWCoinType coin); TW_EXPORT_PROPERTY uint32_t TWCoinTypeSlip44Id(enum TWCoinType coin); +/// SS58Prefix for this coin type +/// +/// \param coin A coin type +/// \return SS58Prefix for the given coin type +TW_EXPORT_PROPERTY +uint32_t TWCoinTypeSS58Prefix(enum TWCoinType coin); + /// public key type for this coin type /// /// \param coin A coin type diff --git a/registry.json b/registry.json index 4de03d0c13e..96dedfb41e8 100644 --- a/registry.json +++ b/registry.json @@ -1022,6 +1022,7 @@ "curve": "ed25519", "publicKeyType": "ed25519", "addressHasher": "keccak256", + "ss58Prefix": 0, "explorer": { "url": "https://polkadot.subscan.io", "txPath": "/extrinsic/", @@ -1129,6 +1130,7 @@ "curve": "ed25519", "publicKeyType": "ed25519", "addressHasher": "keccak256", + "ss58Prefix": 2, "explorer": { "url": "https://kusama.subscan.io", "txPath": "/extrinsic/", diff --git a/src/Aeternity/Entry.cpp b/src/Aeternity/Entry.cpp index cab15ca9d26..0c1fcc95dce 100644 --- a/src/Aeternity/Entry.cpp +++ b/src/Aeternity/Entry.cpp @@ -14,7 +14,7 @@ using namespace std; namespace TW::Aeternity { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Aeternity/Entry.h b/src/Aeternity/Entry.h index b344b0ebb1f..211ffd707c2 100644 --- a/src/Aeternity/Entry.h +++ b/src/Aeternity/Entry.h @@ -14,7 +14,7 @@ namespace TW::Aeternity { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Aion/Entry.cpp b/src/Aion/Entry.cpp index f32ef1e3491..922e368f611 100644 --- a/src/Aion/Entry.cpp +++ b/src/Aion/Entry.cpp @@ -16,7 +16,7 @@ namespace TW::Aion { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Aion/Entry.h b/src/Aion/Entry.h index 1ff2ed22dc1..551016bc95c 100644 --- a/src/Aion/Entry.h +++ b/src/Aion/Entry.h @@ -14,7 +14,7 @@ namespace TW::Aion { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Algorand/Entry.cpp b/src/Algorand/Entry.cpp index 2f090b1ca5b..135434fc5cc 100644 --- a/src/Algorand/Entry.cpp +++ b/src/Algorand/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Algorand { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Algorand/Entry.h b/src/Algorand/Entry.h index a2e2548839b..7fa4a1f91a3 100644 --- a/src/Algorand/Entry.h +++ b/src/Algorand/Entry.h @@ -14,7 +14,7 @@ namespace TW::Algorand { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/AnyAddress.cpp b/src/AnyAddress.cpp index 9bb265cc041..be44ccb2e95 100644 --- a/src/AnyAddress.cpp +++ b/src/AnyAddress.cpp @@ -15,8 +15,9 @@ Data AnyAddress::getData() const { return TW::addressToData(coin, address); } -AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinType coin, const std::string& hrp) { - auto normalized = TW::normalizeAddress(coin, address, hrp); +AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinType coin, const PrefixVariant& prefix) { + const bool hasPrefix = !std::holds_alternative(prefix); + auto normalized = hasPrefix ? TW::normalizeAddress(coin, address, prefix) : TW::normalizeAddress(coin, address); if (normalized.empty()) { return nullptr; } @@ -25,8 +26,15 @@ AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinTyp } AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp) { + auto derivedAddress = TW::deriveAddress(coin, publicKey, TWDerivationDefault, hrp); return new AnyAddress{.address = std::move(derivedAddress), .coin = coin}; } +AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const PrefixVariant& addressPrefix) { + + auto derivedAddress = TW::deriveAddress(coin, publicKey, addressPrefix); + return new AnyAddress{.address = std::move(derivedAddress), .coin = coin}; +} + } // namespace TW diff --git a/src/AnyAddress.h b/src/AnyAddress.h index 7bf773eaf07..a40be466a8e 100644 --- a/src/AnyAddress.h +++ b/src/AnyAddress.h @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace TW { @@ -22,8 +23,9 @@ class AnyAddress { enum TWCoinType coin; - static AnyAddress* createAddress(const std::string& address, enum TWCoinType coin, const std::string& hrp = ""); + static AnyAddress* createAddress(const std::string& address, enum TWCoinType coin, const PrefixVariant& prefix = std::monostate()); static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp = ""); + static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const PrefixVariant& prefix); Data getData() const; }; diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp index 4c299f946a2..44261fc64ac 100644 --- a/src/Aptos/Entry.cpp +++ b/src/Aptos/Entry.cpp @@ -11,7 +11,7 @@ namespace TW::Aptos { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Aptos/Entry.h b/src/Aptos/Entry.h index 0fd1ab1d008..3496e72e8ac 100644 --- a/src/Aptos/Entry.h +++ b/src/Aptos/Entry.h @@ -14,7 +14,7 @@ namespace TW::Aptos { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Binance/Entry.cpp b/src/Binance/Entry.cpp index 48d39fc64c2..a92d97c4a2d 100644 --- a/src/Binance/Entry.cpp +++ b/src/Binance/Entry.cpp @@ -12,7 +12,7 @@ namespace TW::Binance { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Binance/Entry.h b/src/Binance/Entry.h index da927b68166..ee8cd5b158b 100644 --- a/src/Binance/Entry.h +++ b/src/Binance/Entry.h @@ -14,7 +14,7 @@ namespace TW::Binance { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Bitcoin/Entry.cpp b/src/Bitcoin/Entry.cpp index 36ee93a94f6..861e239c61e 100644 --- a/src/Bitcoin/Entry.cpp +++ b/src/Bitcoin/Entry.cpp @@ -13,8 +13,12 @@ namespace TW::Bitcoin { -bool Entry::validateAddress(TWCoinType coin, const std::string& address, byte p2pkh, byte p2sh, - const char* hrp) const { +bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + auto* base58Prefix = std::get_if(&addressPrefix); + auto* hrp = std::get_if(&addressPrefix); + bool isValidBase58 = base58Prefix ? Address::isValid(address, {{base58Prefix->p2pkh}, {base58Prefix->p2sh}}) : false; + bool isValidHrp = hrp ? SegwitAddress::isValid(address, *hrp) : false; + switch (coin) { case TWCoinTypeBitcoin: case TWCoinTypeDigiByte: @@ -23,20 +27,17 @@ bool Entry::validateAddress(TWCoinType coin, const std::string& address, byte p2 case TWCoinTypeQtum: case TWCoinTypeViacoin: case TWCoinTypeBitcoinGold: - return SegwitAddress::isValid(address, hrp) || Address::isValid(address, {{p2pkh}, {p2sh}}); - + return isValidBase58 || isValidHrp; case TWCoinTypeBitcoinCash: - return BitcoinCashAddress::isValid(address) || Address::isValid(address, {{p2pkh}, {p2sh}}); - + return base58Prefix ? isValidBase58 : BitcoinCashAddress::isValid(address); case TWCoinTypeECash: - return ECashAddress::isValid(address) || Address::isValid(address, {{p2pkh}, {p2sh}}); - + return base58Prefix ? isValidBase58 : ECashAddress::isValid(address); case TWCoinTypeDash: case TWCoinTypeDogecoin: case TWCoinTypeRavencoin: case TWCoinTypeFiro: default: - return Address::isValid(address, {{p2pkh}, {p2sh}}); + return isValidBase58; } } @@ -63,7 +64,7 @@ std::string Entry::normalizeAddress(TWCoinType coin, const std::string& address) } std::string Entry::deriveAddress(TWCoinType coin, TWDerivation derivation, const PublicKey& publicKey, - byte p2pkh, const char* hrp) const { + byte p2pkh, const char* hrp) const { switch (coin) { case TWCoinTypeBitcoin: case TWCoinTypeLitecoin: diff --git a/src/Bitcoin/Entry.h b/src/Bitcoin/Entry.h index 713f97685d2..aec706f5d5f 100644 --- a/src/Bitcoin/Entry.h +++ b/src/Bitcoin/Entry.h @@ -15,8 +15,7 @@ namespace TW::Bitcoin { /// includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, - const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string normalizeAddress(TWCoinType coin, const std::string& address) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const { diff --git a/src/Cardano/Entry.cpp b/src/Cardano/Entry.cpp index 154b44ac407..2bf09dde28d 100644 --- a/src/Cardano/Entry.cpp +++ b/src/Cardano/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Cardano { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return AddressV3::isValidLegacy(address); } diff --git a/src/Cardano/Entry.h b/src/Cardano/Entry.h index 94766b32303..7cac8650256 100644 --- a/src/Cardano/Entry.h +++ b/src/Cardano/Entry.h @@ -14,7 +14,7 @@ namespace TW::Cardano { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Coin.cpp b/src/Coin.cpp index efe10e47e56..9b88d7eb252 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -173,32 +173,62 @@ const Derivation CoinInfo::derivationByName(TWDerivation nameIn) const { return Derivation(); } -bool TW::validateAddress(TWCoinType coin, const std::string& string, const char* hrp) { +bool TW::validateAddress(TWCoinType coin, const string& address, const PrefixVariant& prefix) { + // dispatch + auto* dispatcher = coinDispatcher(coin); + assert(dispatcher != nullptr); + return dispatcher->validateAddress(coin, address, prefix); +} + +bool TW::validateAddress(TWCoinType coin, const std::string& string) { + const auto* hrp = stringForHRP(TW::hrp(coin)); auto p2pkh = TW::p2pkhPrefix(coin); auto p2sh = TW::p2shPrefix(coin); // dispatch auto* dispatcher = coinDispatcher(coin); assert(dispatcher != nullptr); - return dispatcher->validateAddress(coin, string, p2pkh, p2sh, hrp); + bool isValid = false; + // First check HRP. + if (hrp != nullptr && !std::string(hrp).empty()) { + isValid = dispatcher->validateAddress(coin, string, Bech32Prefix(hrp)); + } + // Then check UTXO + if ((p2pkh != 0 || p2sh != 0) && !isValid) { + return isValid || dispatcher->validateAddress(coin, string, Base58Prefix{.p2pkh = p2pkh, .p2sh = p2sh}); + } + // Then check normal + if (!isValid) { + isValid = dispatcher->validateAddress(coin, string, std::monostate()); + } + return isValid; } -bool TW::validateAddress(TWCoinType coin, const std::string& string) { - const auto* hrp = stringForHRP(TW::hrp(coin)); - return TW::validateAddress(coin, string, hrp); +namespace TW::internal { + inline std::string normalizeAddress(TWCoinType coin, const string& address) { + // dispatch + auto* dispatcher = coinDispatcher(coin); + assert(dispatcher != nullptr); + return dispatcher->normalizeAddress(coin, address); + } } -std::string TW::normalizeAddress(TWCoinType coin, const std::string& address, const std::string& hrp) { - const char* rawHrp = hrp.empty() ? stringForHRP(TW::hrp(coin)) : hrp.c_str(); - if (!TW::validateAddress(coin, address, rawHrp)) { +std::string TW::normalizeAddress(TWCoinType coin, const string& address) {; + if (!TW::validateAddress(coin, address)) { // invalid address, not normalizing return ""; } - // dispatch - auto* dispatcher = coinDispatcher(coin); - assert(dispatcher != nullptr); - return dispatcher->normalizeAddress(coin, address); + return internal::normalizeAddress(coin, address); +} + +std::string TW::normalizeAddress(TWCoinType coin, const std::string& address, const PrefixVariant& prefix) { + if (!TW::validateAddress(coin, address, prefix)) { + // invalid address, not normalizing + return ""; + } + + return internal::normalizeAddress(coin, address); } std::string TW::deriveAddress(TWCoinType coin, const PrivateKey& privateKey) { @@ -210,6 +240,12 @@ std::string TW::deriveAddress(TWCoinType coin, const PrivateKey& privateKey, TWD return TW::deriveAddress(coin, privateKey.getPublicKey(keyType), derivation); } +std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey, const PrefixVariant& addressPrefix, TWDerivation derivation) { + auto const* dispatcher = coinDispatcher(coin); + assert(dispatcher != nullptr); + return dispatcher->deriveAddress(coin, publicKey, derivation, addressPrefix); +} + std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const std::string& hrp) { auto p2pkh = TW::p2pkhPrefix(coin); const char* hrpRaw = [&hrp, coin]() { @@ -353,6 +389,10 @@ uint32_t TW::slip44Id(TWCoinType coin) { return getCoinInfo(coin).slip44; } +std::uint32_t TW::ss58Prefix(TWCoinType coin) { + return getCoinInfo(coin).ss58Prefix; +} + TWString* _Nullable TWCoinTypeConfigurationGetSymbol(enum TWCoinType coin) { return TWStringCreateWithUTF8Bytes(getCoinInfo(coin).symbol); } diff --git a/src/Coin.h b/src/Coin.h index aba8e7adef5..3a994267d1b 100644 --- a/src/Coin.h +++ b/src/Coin.h @@ -33,10 +33,11 @@ std::vector getCoinTypes(); bool validateAddress(TWCoinType coin, const std::string& address); /// Validates an address for a particular coin. -bool validateAddress(TWCoinType coin, const std::string& address, const char* hrp); +bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& prefix); /// Validates and normalizes an address for a particular coin. -std::string normalizeAddress(TWCoinType coin, const std::string& address, const std::string& hrp = ""); +std::string normalizeAddress(TWCoinType coin, const std::string& address); +std::string normalizeAddress(TWCoinType coin, const std::string& address, const PrefixVariant& prefix); /// Returns the blockchain for a coin type. TWBlockchain blockchain(TWCoinType coin); @@ -79,6 +80,8 @@ std::string deriveAddress(TWCoinType coin, const PrivateKey& privateKey, TWDeriv /// Derives the address for a particular coin from the public key, with given derivation. std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation = TWDerivationDefault, const std::string& hrp = ""); +/// Derives the address for a particular coin from the public key, with given derivation and explicit addressPrefix. +std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, const PrefixVariant& addressPrefix, TWDerivation derivation = TWDerivationDefault); /// Returns the binary representation of a string address Data addressToData(TWCoinType coin, const std::string& address); @@ -104,6 +107,9 @@ byte p2shPrefix(TWCoinType coin); /// Returns human readable part for a coin type. enum TWHRP hrp(TWCoinType coin); +/// Returns the ss58 prefix of a coin type. +std::uint32_t ss58Prefix(TWCoinType coin); + /// Returns chain ID. const char* chainId(TWCoinType coin); @@ -155,6 +161,7 @@ struct CoinInfo { const char* explorerTransactionUrl; const char* explorerAccountUrl; uint32_t slip44; + std::uint32_t ss58Prefix; // returns default derivation const Derivation defaultDerivation() const { diff --git a/src/CoinEntry.h b/src/CoinEntry.h index b0ab4bd72d3..99a56942ff8 100644 --- a/src/CoinEntry.h +++ b/src/CoinEntry.h @@ -17,22 +17,36 @@ #include #include +#include #include namespace TW { typedef std::vector> HashPubkeyList; +struct Base58Prefix { + TW::byte p2pkh; + TW::byte p2sh; +}; + +using Bech32Prefix = const char *; +using SS58Prefix = uint32_t; + +using PrefixVariant = std::variant; + /// Interface for coin-specific entry, used to dispatch calls to coins /// Implement this for all coins. class CoinEntry { public: virtual ~CoinEntry() noexcept = default; - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const = 0; + virtual bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const = 0; // normalizeAddress is optional, it may leave this default, no-change implementation virtual std::string normalizeAddress([[maybe_unused]] TWCoinType coin, const std::string& address) const { return address; } // Address derivation, default derivation virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const = 0; + virtual std::string deriveAddress([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return ""; + }; // Address derivation, by default invoking default virtual std::string deriveAddress(TWCoinType coin, [[maybe_unused]] TWDerivation derivation, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const { return deriveAddress(coin, publicKey, p2pkh, hrp); diff --git a/src/Cosmos/Entry.cpp b/src/Cosmos/Entry.cpp index 6cf9026fddd..7de1049e24e 100644 --- a/src/Cosmos/Entry.cpp +++ b/src/Cosmos/Entry.cpp @@ -16,11 +16,14 @@ namespace TW::Cosmos { // 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* hrp) const { - if (hrpForString(hrp) != TWHRPUnknown) { - return Address::isValid(coin, address); +bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + if (auto* hrp = std::get_if(&addressPrefix); hrp) { + if (hrpForString(*hrp) != TWHRPUnknown) { + return Address::isValid(coin, address); + } + return Address::isValid(address, *hrp); } - return Address::isValid(address, hrp); + return false; } string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char* hrp) const { diff --git a/src/Cosmos/Entry.h b/src/Cosmos/Entry.h index e5be0796622..599bb623f01 100644 --- a/src/Cosmos/Entry.h +++ b/src/Cosmos/Entry.h @@ -14,7 +14,7 @@ namespace TW::Cosmos { /// 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: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const final; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const final; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const final; Data addressToData(TWCoinType coin, const std::string& address) const final; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; diff --git a/src/Decred/Entry.cpp b/src/Decred/Entry.cpp index 2bfc7149e39..36e0b21d1e7 100644 --- a/src/Decred/Entry.cpp +++ b/src/Decred/Entry.cpp @@ -11,7 +11,7 @@ namespace TW::Decred { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] TW::byte p2sh, [[maybe_unused]] const char* hrp) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Decred/Entry.h b/src/Decred/Entry.h index 0d21789afa8..d6d2dccb262 100644 --- a/src/Decred/Entry.h +++ b/src/Decred/Entry.h @@ -14,11 +14,11 @@ namespace TW::Decred { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - Data addressToData(TWCoinType coin, const std::string& address) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Decred diff --git a/src/EOS/Entry.cpp b/src/EOS/Entry.cpp index fa3af508bb4..f2ecbbfa138 100644 --- a/src/EOS/Entry.cpp +++ b/src/EOS/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::EOS { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/EOS/Entry.h b/src/EOS/Entry.h index 63823c8c020..3d5c1fda040 100644 --- a/src/EOS/Entry.h +++ b/src/EOS/Entry.h @@ -14,9 +14,9 @@ namespace TW::EOS { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::EOS diff --git a/src/Elrond/Entry.cpp b/src/Elrond/Entry.cpp index 2535f0bec96..ff04f37b8ed 100644 --- a/src/Elrond/Entry.cpp +++ b/src/Elrond/Entry.cpp @@ -15,7 +15,7 @@ using namespace std; namespace TW::Elrond { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Elrond/Entry.h b/src/Elrond/Entry.h index e0c4ed7b0a1..27b1270a462 100644 --- a/src/Elrond/Entry.h +++ b/src/Elrond/Entry.h @@ -12,14 +12,14 @@ 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 final: public CoinEntry { +class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - Data addressToData(TWCoinType coin, const std::string& address) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - bool supportsJSONSigning() const { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Elrond diff --git a/src/Ethereum/Entry.cpp b/src/Ethereum/Entry.cpp index f5a7c7ab87e..789384a4068 100644 --- a/src/Ethereum/Entry.cpp +++ b/src/Ethereum/Entry.cpp @@ -15,7 +15,7 @@ namespace TW::Ethereum { using namespace std; -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Ethereum/Entry.h b/src/Ethereum/Entry.h index 23a31b2c8d1..b92042e91dc 100644 --- a/src/Ethereum/Entry.h +++ b/src/Ethereum/Entry.h @@ -14,7 +14,7 @@ namespace TW::Ethereum { /// 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: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const final; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const final; std::string normalizeAddress(TWCoinType coin, const std::string& address) const final; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const final; Data addressToData(TWCoinType coin, const std::string& address) const final; diff --git a/src/Everscale/Entry.cpp b/src/Everscale/Entry.cpp index e17aa9d5997..216cca93ec4 100644 --- a/src/Everscale/Entry.cpp +++ b/src/Everscale/Entry.cpp @@ -17,7 +17,7 @@ namespace TW::Everscale { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Everscale/Entry.h b/src/Everscale/Entry.h index c83a0d133ad..d24d55ebceb 100644 --- a/src/Everscale/Entry.h +++ b/src/Everscale/Entry.h @@ -14,7 +14,7 @@ namespace TW::Everscale { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string normalizeAddress(TWCoinType coin, const std::string& address) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* d) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/FIO/Entry.cpp b/src/FIO/Entry.cpp index a9194cb53ae..c435b98ff4f 100644 --- a/src/FIO/Entry.cpp +++ b/src/FIO/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::FIO { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/FIO/Entry.h b/src/FIO/Entry.h index cb9ab8c38a2..49fa7e95002 100644 --- a/src/FIO/Entry.h +++ b/src/FIO/Entry.h @@ -14,7 +14,7 @@ namespace TW::FIO { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Filecoin/Entry.cpp b/src/Filecoin/Entry.cpp index 5ab6065a3ca..0317621d6a4 100644 --- a/src/Filecoin/Entry.cpp +++ b/src/Filecoin/Entry.cpp @@ -13,8 +13,7 @@ namespace TW::Filecoin { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, - const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Filecoin/Entry.h b/src/Filecoin/Entry.h index a989db0676e..7db1f33b6b0 100644 --- a/src/Filecoin/Entry.h +++ b/src/Filecoin/Entry.h @@ -15,8 +15,7 @@ namespace TW::Filecoin { /// includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, - TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Groestlcoin/Entry.cpp b/src/Groestlcoin/Entry.cpp index ae12284620a..6235c416271 100644 --- a/src/Groestlcoin/Entry.cpp +++ b/src/Groestlcoin/Entry.cpp @@ -12,8 +12,11 @@ namespace TW::Groestlcoin { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const { - return TW::Bitcoin::SegwitAddress::isValid(address, hrp) || Address::isValid(address, {p2pkh, p2sh}); +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + if (auto* prefix = std::get_if(&addressPrefix); prefix) { + return Address::isValid(address, {prefix->p2pkh, prefix->p2sh}); + } + return TW::Bitcoin::SegwitAddress::isValid(address, std::get(addressPrefix)); } std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TW::byte p2pkh, const char* hrp) const { diff --git a/src/Groestlcoin/Entry.h b/src/Groestlcoin/Entry.h index 069cc75074e..37ea8ed1bc2 100644 --- a/src/Groestlcoin/Entry.h +++ b/src/Groestlcoin/Entry.h @@ -14,7 +14,7 @@ namespace TW::Groestlcoin { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Harmony/Entry.cpp b/src/Harmony/Entry.cpp index ad652105739..750f4d735ab 100644 --- a/src/Harmony/Entry.cpp +++ b/src/Harmony/Entry.cpp @@ -16,7 +16,7 @@ namespace TW::Harmony { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Harmony/Entry.h b/src/Harmony/Entry.h index 1feefb3ca70..167779f8292 100644 --- a/src/Harmony/Entry.h +++ b/src/Harmony/Entry.h @@ -14,7 +14,7 @@ namespace TW::Harmony { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Icon/Address.cpp b/src/Icon/Address.cpp index 6f219a3b537..79a680098d2 100644 --- a/src/Icon/Address.cpp +++ b/src/Icon/Address.cpp @@ -12,14 +12,14 @@ namespace TW::Icon { -static const std::string addressPrefix = "hx"; +static const std::string gAddressPrefix = "hx"; static const std::string contractPrefix = "cx"; bool Address::isValid(const std::string& string) { if (string.size() != Address::size * 2 + 2) { return false; } - if (!std::equal(addressPrefix.begin(), addressPrefix.end(), string.begin()) && + if (!std::equal(gAddressPrefix.begin(), gAddressPrefix.end(), string.begin()) && !std::equal(contractPrefix.begin(), contractPrefix.end(), string.begin())) { return false; } @@ -31,7 +31,7 @@ Address::Address(const std::string& string) { throw std::invalid_argument("Invalid address data"); } - if (std::equal(addressPrefix.begin(), addressPrefix.end(), string.begin())) { + if (std::equal(gAddressPrefix.begin(), gAddressPrefix.end(), string.begin())) { type = TypeAddress; } else if (std::equal(contractPrefix.begin(), contractPrefix.end(), string.begin())) { type = TypeContract; @@ -53,7 +53,7 @@ Address::Address(const PublicKey& publicKey, enum AddressType type) std::string Address::string() const { switch (type) { case TypeAddress: - return addressPrefix + hex(bytes); + return gAddressPrefix + hex(bytes); case TypeContract: return contractPrefix + hex(bytes); default: diff --git a/src/Icon/Entry.cpp b/src/Icon/Entry.cpp index 66c6ff5d2ad..4df47c1728a 100644 --- a/src/Icon/Entry.cpp +++ b/src/Icon/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Icon { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Icon/Entry.h b/src/Icon/Entry.h index 9547997f419..feeed6df284 100644 --- a/src/Icon/Entry.h +++ b/src/Icon/Entry.h @@ -14,7 +14,7 @@ namespace TW::Icon { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/IoTeX/Entry.cpp b/src/IoTeX/Entry.cpp index 5e427d85a67..85db67fe256 100644 --- a/src/IoTeX/Entry.cpp +++ b/src/IoTeX/Entry.cpp @@ -15,7 +15,7 @@ namespace TW::IoTeX { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/IoTeX/Entry.h b/src/IoTeX/Entry.h index ecf04236fc7..8793f9bcab9 100644 --- a/src/IoTeX/Entry.h +++ b/src/IoTeX/Entry.h @@ -14,7 +14,7 @@ namespace TW::IoTeX { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Kusama/Entry.cpp b/src/Kusama/Entry.cpp index 28e56d9bfb0..98876e41124 100644 --- a/src/Kusama/Entry.cpp +++ b/src/Kusama/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Kusama { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Kusama/Entry.h b/src/Kusama/Entry.h index f9d566239a1..0398c6f4acf 100644 --- a/src/Kusama/Entry.h +++ b/src/Kusama/Entry.h @@ -14,7 +14,7 @@ namespace TW::Kusama { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/NEAR/Entry.cpp b/src/NEAR/Entry.cpp index c33987cc422..63d69948969 100644 --- a/src/NEAR/Entry.cpp +++ b/src/NEAR/Entry.cpp @@ -16,7 +16,7 @@ namespace TW::NEAR { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/NEAR/Entry.h b/src/NEAR/Entry.h index 20a0870762c..41c5f52fc20 100644 --- a/src/NEAR/Entry.h +++ b/src/NEAR/Entry.h @@ -14,7 +14,7 @@ namespace TW::NEAR { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string normalizeAddress(TWCoinType coin, const std::string& address) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; diff --git a/src/NEO/Entry.cpp b/src/NEO/Entry.cpp index f9e55d72637..27a5d53fd92 100644 --- a/src/NEO/Entry.cpp +++ b/src/NEO/Entry.cpp @@ -14,7 +14,7 @@ using namespace std; namespace TW::NEO { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const string& address, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] TW::byte p2sh, [[maybe_unused]] const char* hrp) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/NEO/Entry.h b/src/NEO/Entry.h index 6a396f753c2..8881a48a2ba 100644 --- a/src/NEO/Entry.h +++ b/src/NEO/Entry.h @@ -14,7 +14,7 @@ namespace TW::NEO { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/NULS/Entry.cpp b/src/NULS/Entry.cpp index db59dba1bb1..27aba6043d9 100644 --- a/src/NULS/Entry.cpp +++ b/src/NULS/Entry.cpp @@ -15,7 +15,7 @@ namespace TW::NULS { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/NULS/Entry.h b/src/NULS/Entry.h index b11bcc629bb..1060cc41c8e 100644 --- a/src/NULS/Entry.h +++ b/src/NULS/Entry.h @@ -14,7 +14,7 @@ namespace TW::NULS { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Nano/Entry.cpp b/src/Nano/Entry.cpp index 6cfdb73e52b..86858b9b1d3 100644 --- a/src/Nano/Entry.cpp +++ b/src/Nano/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Nano { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Nano/Entry.h b/src/Nano/Entry.h index 1a27f029e23..9ca84e41eb0 100644 --- a/src/Nano/Entry.h +++ b/src/Nano/Entry.h @@ -14,7 +14,7 @@ namespace TW::Nano { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Nebulas/Entry.cpp b/src/Nebulas/Entry.cpp index a8b4571e527..0487bb74e7d 100644 --- a/src/Nebulas/Entry.cpp +++ b/src/Nebulas/Entry.cpp @@ -14,7 +14,7 @@ using namespace std; namespace TW::Nebulas { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Nebulas/Entry.h b/src/Nebulas/Entry.h index 0bdcaded71c..7fa88305c1c 100644 --- a/src/Nebulas/Entry.h +++ b/src/Nebulas/Entry.h @@ -14,7 +14,7 @@ namespace TW::Nebulas { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Nervos/Entry.cpp b/src/Nervos/Entry.cpp index 44e725ae6a3..b4e9b071d55 100644 --- a/src/Nervos/Entry.cpp +++ b/src/Nervos/Entry.cpp @@ -12,9 +12,9 @@ namespace TW::Nervos { using namespace std; -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, byte, byte, - const char* hrp) const { - return Address::isValid(address, hrp); +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + auto* hrpPrefix = std::get_if(&addressPrefix); + return Address::isValid(address, hrpPrefix ? *hrpPrefix : HRP_NERVOS); } string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, byte, diff --git a/src/Nervos/Entry.h b/src/Nervos/Entry.h index 0d3919dcefb..e91a777e7fa 100644 --- a/src/Nervos/Entry.h +++ b/src/Nervos/Entry.h @@ -17,8 +17,7 @@ namespace TW::Nervos { /// includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, byte p2pkh, byte p2sh, - const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Nimiq/Entry.cpp b/src/Nimiq/Entry.cpp index 92279053f4f..c063fe2d977 100644 --- a/src/Nimiq/Entry.cpp +++ b/src/Nimiq/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Nimiq { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Nimiq/Entry.h b/src/Nimiq/Entry.h index cad09e0fc65..6e19d3e24af 100644 --- a/src/Nimiq/Entry.h +++ b/src/Nimiq/Entry.h @@ -14,7 +14,7 @@ namespace TW::Nimiq { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Oasis/Entry.cpp b/src/Oasis/Entry.cpp index 3dbaeeb4d24..a9e1f4172bf 100644 --- a/src/Oasis/Entry.cpp +++ b/src/Oasis/Entry.cpp @@ -15,7 +15,7 @@ namespace TW::Oasis { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Oasis/Entry.h b/src/Oasis/Entry.h index c4b3fb6240f..4ae19abbf0a 100644 --- a/src/Oasis/Entry.h +++ b/src/Oasis/Entry.h @@ -14,7 +14,7 @@ namespace TW::Oasis { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Ontology/Entry.cpp b/src/Ontology/Entry.cpp index 9f4de742362..51d251e24d0 100644 --- a/src/Ontology/Entry.cpp +++ b/src/Ontology/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Ontology { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Ontology/Entry.h b/src/Ontology/Entry.h index 68df17b8baf..836a0f391e3 100644 --- a/src/Ontology/Entry.h +++ b/src/Ontology/Entry.h @@ -14,7 +14,7 @@ namespace TW::Ontology { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Polkadot/Address.h b/src/Polkadot/Address.h index 10f3828c2a0..f695ad85798 100644 --- a/src/Polkadot/Address.h +++ b/src/Polkadot/Address.h @@ -19,12 +19,15 @@ class Address: public SS58Address { public: /// Determines whether a string makes a valid address. static bool isValid(const std::string& string) { return SS58Address::isValid(string, TWSS58AddressTypePolkadot); } + static bool isValid(const std::string& string, uint32_t ss58Prefix) { return SS58Address::isValid(string, ss58Prefix); } /// Initializes a Polkadot address with a string representation. - Address(const std::string& string): SS58Address(string, TWSS58AddressTypePolkadot) {} + explicit Address(const std::string& string): SS58Address(string, TWSS58AddressTypePolkadot) {} /// Initializes a Polkadot address with a public key. - Address(const PublicKey& publicKey): SS58Address(publicKey, TWSS58AddressTypePolkadot) {} + explicit Address(const PublicKey& publicKey): SS58Address(publicKey, TWSS58AddressTypePolkadot) {} + /// Initializes a Polkadot address with a public key and a given ss58Prefix. + explicit Address(const PublicKey& publicKey, std::uint32_t ss58Prefix): SS58Address(publicKey, ss58Prefix) {} }; } // namespace TW::Polkadot diff --git a/src/Polkadot/Entry.cpp b/src/Polkadot/Entry.cpp index f97ae843ca9..db51b204ec1 100644 --- a/src/Polkadot/Entry.cpp +++ b/src/Polkadot/Entry.cpp @@ -13,7 +13,10 @@ namespace TW::Polkadot { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + if (auto* prefix = std::get_if(&addressPrefix); prefix) { + return Address::isValid(address, *prefix); + } return Address::isValid(address); } @@ -30,4 +33,11 @@ void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::D signTemplate(dataIn, dataOut); } +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { + if (auto* ss58Prefix = std::get_if(&addressPrefix); ss58Prefix) { + return Address(publicKey, *ss58Prefix).string(); + } + return ""; +} + } // namespace TW::Polkadot diff --git a/src/Polkadot/Entry.h b/src/Polkadot/Entry.h index d36ac8845af..5c19d70c6db 100644 --- a/src/Polkadot/Entry.h +++ b/src/Polkadot/Entry.h @@ -14,10 +14,11 @@ namespace TW::Polkadot { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - Data addressToData(TWCoinType coin, const std::string& address) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Polkadot diff --git a/src/Polkadot/SS58Address.cpp b/src/Polkadot/SS58Address.cpp index 34a25f7c263..2c99b764ba9 100644 --- a/src/Polkadot/SS58Address.cpp +++ b/src/Polkadot/SS58Address.cpp @@ -34,7 +34,7 @@ bool SS58Address::isValid(const std::string& string, uint32_t network) { template Data SS58Address::computeChecksum(const T& data) { - auto prefix = Data(SS58Prefix.begin(), SS58Prefix.end()); + auto prefix = Data(gSS58Prefix.begin(), gSS58Prefix.end()); append(prefix, Data(data.begin(), data.end())); auto hash = Hash::blake2b(prefix, 64); auto checksum = Data(checksumSize); diff --git a/src/Polkadot/SS58Address.h b/src/Polkadot/SS58Address.h index 20d4e292f70..4feaaacd760 100644 --- a/src/Polkadot/SS58Address.h +++ b/src/Polkadot/SS58Address.h @@ -12,7 +12,7 @@ #include -inline const std::string SS58Prefix{"SS58PRE"}; +inline const std::string gSS58Prefix{"SS58PRE"}; namespace TW { diff --git a/src/Ronin/Entry.cpp b/src/Ronin/Entry.cpp index d69ac356390..6eaf45cf2d8 100644 --- a/src/Ronin/Entry.cpp +++ b/src/Ronin/Entry.cpp @@ -14,7 +14,7 @@ using namespace std; namespace TW::Ronin { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Ronin/Entry.h b/src/Ronin/Entry.h index 9bb7f96aa40..8723a549db1 100644 --- a/src/Ronin/Entry.h +++ b/src/Ronin/Entry.h @@ -13,7 +13,7 @@ namespace TW::Ronin { /// Entry point for Ronin (EVM side chain) class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string normalizeAddress(TWCoinType coin, const std::string& address) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; diff --git a/src/Solana/Entry.cpp b/src/Solana/Entry.cpp index 765c9c1927f..1e216d426ab 100644 --- a/src/Solana/Entry.cpp +++ b/src/Solana/Entry.cpp @@ -16,7 +16,7 @@ namespace TW::Solana { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Solana/Entry.h b/src/Solana/Entry.h index b6bc8b4f1d4..2735b866e69 100644 --- a/src/Solana/Entry.h +++ b/src/Solana/Entry.h @@ -14,7 +14,7 @@ namespace TW::Solana { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Stellar/Entry.cpp b/src/Stellar/Entry.cpp index a2412dcb5dd..ce6cded2f72 100644 --- a/src/Stellar/Entry.cpp +++ b/src/Stellar/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Stellar { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Stellar/Entry.h b/src/Stellar/Entry.h index f4236c325df..14f9dc4b6cd 100644 --- a/src/Stellar/Entry.h +++ b/src/Stellar/Entry.h @@ -14,7 +14,7 @@ namespace TW::Stellar { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Tezos/Entry.cpp b/src/Tezos/Entry.cpp index 71aa8246a81..19a6dc9f66c 100644 --- a/src/Tezos/Entry.cpp +++ b/src/Tezos/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Tezos { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Tezos/Entry.h b/src/Tezos/Entry.h index bea87ca0aa3..ae744e64236 100644 --- a/src/Tezos/Entry.h +++ b/src/Tezos/Entry.h @@ -14,7 +14,7 @@ namespace TW::Tezos { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/Tron/Entry.cpp b/src/Tron/Entry.cpp index 056728c4bc6..8a0feb4d29b 100644 --- a/src/Tron/Entry.cpp +++ b/src/Tron/Entry.cpp @@ -14,7 +14,7 @@ using namespace std; namespace TW::Tron { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Tron/Entry.h b/src/Tron/Entry.h index 0d25c65da8b..531f2ef9127 100644 --- a/src/Tron/Entry.h +++ b/src/Tron/Entry.h @@ -14,7 +14,7 @@ namespace TW::Tron { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Waves/Entry.cpp b/src/Waves/Entry.cpp index 9635e2d30cd..30b5e730871 100644 --- a/src/Waves/Entry.cpp +++ b/src/Waves/Entry.cpp @@ -15,7 +15,7 @@ namespace TW::Waves { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Waves/Entry.h b/src/Waves/Entry.h index ea114e04c45..fe30cd62120 100644 --- a/src/Waves/Entry.h +++ b/src/Waves/Entry.h @@ -14,7 +14,7 @@ namespace TW::Waves { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/XRP/Entry.cpp b/src/XRP/Entry.cpp index b28a843c670..7fd7c10f85e 100644 --- a/src/XRP/Entry.cpp +++ b/src/XRP/Entry.cpp @@ -14,7 +14,7 @@ namespace TW::Ripple { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address) || XAddress::isValid(address); } diff --git a/src/XRP/Entry.h b/src/XRP/Entry.h index 6736133286e..f30c07fb4bc 100644 --- a/src/XRP/Entry.h +++ b/src/XRP/Entry.h @@ -14,7 +14,7 @@ namespace TW::Ripple { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Zcash/Entry.cpp b/src/Zcash/Entry.cpp index ff5f1ac21ce..883cf768027 100644 --- a/src/Zcash/Entry.cpp +++ b/src/Zcash/Entry.cpp @@ -11,7 +11,7 @@ namespace TW::Zcash { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const std::string& address, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] TW::byte p2sh, [[maybe_unused]] const char* hrp) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return TAddress::isValid(address); } diff --git a/src/Zcash/Entry.h b/src/Zcash/Entry.h index 626f8105131..c50bcfc8ea7 100644 --- a/src/Zcash/Entry.h +++ b/src/Zcash/Entry.h @@ -14,7 +14,7 @@ namespace TW::Zcash { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Zilliqa/Entry.cpp b/src/Zilliqa/Entry.cpp index 9ab22662f0d..249c2e2b5b0 100644 --- a/src/Zilliqa/Entry.cpp +++ b/src/Zilliqa/Entry.cpp @@ -13,7 +13,7 @@ namespace TW::Zilliqa { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, byte, byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Zilliqa/Entry.h b/src/Zilliqa/Entry.h index fa1335f5838..90eda0931d1 100644 --- a/src/Zilliqa/Entry.h +++ b/src/Zilliqa/Entry.h @@ -14,7 +14,7 @@ namespace TW::Zilliqa { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index 6ad7a9fb9ce..d67496008d4 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -21,6 +21,11 @@ bool TWAnyAddressIsValid(TWString* _Nonnull string, enum TWCoinType coin) { return TW::validateAddress(coin, address); } +bool TWAnyAddressIsValidSS58([[maybe_unused]] TWString* string, [[maybe_unused]] enum TWCoinType coin, [[maybe_unused]] uint32_t ss58Prefix) { + const auto& address = *reinterpret_cast(string); + return TW::validateAddress(coin, address, ss58Prefix); +} + bool TWAnyAddressIsValidBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp) { const auto& address = *reinterpret_cast(string); const auto& hrpStr = *reinterpret_cast(hrp); @@ -41,7 +46,17 @@ struct TWAnyAddress* _Nullable TWAnyAddressCreateBech32(TWString* _Nonnull strin enum TWCoinType coin, TWString* _Nonnull hrp) { const auto& address = *reinterpret_cast(string); const auto& hrpStr = *reinterpret_cast(hrp); - auto *impl = TW::AnyAddress::createAddress(address, coin, hrpStr); + auto *impl = TW::AnyAddress::createAddress(address, coin, hrpStr.c_str()); + if (impl == nullptr) { + return nullptr; + } + return new TWAnyAddress{impl}; +} + + +struct TWAnyAddress* TWAnyAddressCreateSS58(TWString* _Nonnull string, enum TWCoinType coin, uint32_t ss58Prefix) { + const auto& address = *reinterpret_cast(string); + auto *impl = TW::AnyAddress::createAddress(address, coin, ss58Prefix); if (impl == nullptr) { return nullptr; } @@ -59,6 +74,10 @@ struct TWAnyAddress* _Nonnull TWAnyAddressCreateBech32WithPublicKey( return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, hrpStr)}; } +struct TWAnyAddress* TWAnyAddressCreateSS58WithPublicKey(struct TWPublicKey* publicKey, enum TWCoinType coin, uint32_t ss58Prefix) { + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, ss58Prefix)}; +} + void TWAnyAddressDelete(struct TWAnyAddress* _Nonnull address) { delete address->impl; delete address; diff --git a/src/interface/TWCoinType.cpp b/src/interface/TWCoinType.cpp index 2d58d5a7be5..e63373a98c8 100644 --- a/src/interface/TWCoinType.cpp +++ b/src/interface/TWCoinType.cpp @@ -82,3 +82,7 @@ uint32_t TWCoinTypeSlip44Id(enum TWCoinType coin) { enum TWPublicKeyType TWCoinTypePublicKeyType(enum TWCoinType coin) { return TW::publicKeyType(coin); } + +uint32_t TWCoinTypeSS58Prefix(enum TWCoinType coin) { + return TW::ss58Prefix(coin); +} diff --git a/src/proto/Polkadot.proto b/src/proto/Polkadot.proto index 178a26485de..8fa16926363 100644 --- a/src/proto/Polkadot.proto +++ b/src/proto/Polkadot.proto @@ -3,12 +3,6 @@ syntax = "proto3"; package TW.Polkadot.Proto; option java_package = "wallet.core.jni.proto"; -// Known networks -enum Network { - POLKADOT = 0; - KUSAMA = 2; -} - // Destination options for reward enum RewardDestination { STAKED = 0; @@ -142,7 +136,7 @@ message SigningInput { bytes private_key = 8; // Network type - Network network = 9; + uint32 network = 9; // Payload message oneof message_oneof { diff --git a/swift/Tests/Blockchains/KusamaTests.swift b/swift/Tests/Blockchains/KusamaTests.swift index 0b93a1cd118..ae5fd1ea397 100644 --- a/swift/Tests/Blockchains/KusamaTests.swift +++ b/swift/Tests/Blockchains/KusamaTests.swift @@ -51,7 +51,7 @@ class KusamaTests: XCTestCase { $0.value = Data(hexString: "0x02540be400")! } } - $0.network = .kusama + $0.network = CoinType.kusama.ss58Prefix $0.transactionVersion = 2 $0.privateKey = key.data } diff --git a/swift/Tests/Blockchains/PolkadotTests.swift b/swift/Tests/Blockchains/PolkadotTests.swift index aae9c432a70..c4414e5f607 100644 --- a/swift/Tests/Blockchains/PolkadotTests.swift +++ b/swift/Tests/Blockchains/PolkadotTests.swift @@ -44,7 +44,7 @@ class PolkadotTests: XCTestCase { $0.blockHash = Data(hexString: "0x7d5fa17b70251d0806f26156b1b698dfd09e040642fa092595ce0a78e9e84fcd")! $0.nonce = 1 $0.specVersion = 28 - $0.network = .polkadot + $0.network = CoinType.polkadot.ss58Prefix $0.transactionVersion = 6 $0.privateKey = key.data $0.era = PolkadotEra.with { @@ -73,7 +73,7 @@ class PolkadotTests: XCTestCase { $0.blockHash = genesisHash $0.nonce = 0 $0.specVersion = 17 - $0.network = .polkadot + $0.network = CoinType.polkadot.ss58Prefix $0.transactionVersion = 3 $0.privateKey = key.data $0.stakingCall.bond = PolkadotStaking.Bond.with { @@ -97,7 +97,7 @@ class PolkadotTests: XCTestCase { $0.blockHash = genesisHash $0.nonce = 4 $0.specVersion = 30 - $0.network = .polkadot + $0.network = CoinType.polkadot.ss58Prefix $0.transactionVersion = 7 $0.privateKey = key.data $0.stakingCall.bondAndNominate = PolkadotStaking.BondAndNominate.with { @@ -125,7 +125,7 @@ class PolkadotTests: XCTestCase { $0.blockHash = genesisHash $0.nonce = 5 $0.specVersion = 30 - $0.network = .polkadot + $0.network = CoinType.polkadot.ss58Prefix $0.transactionVersion = 7 $0.privateKey = key.data $0.stakingCall.bondExtra = PolkadotStaking.BondExtra.with { @@ -151,7 +151,7 @@ class PolkadotTests: XCTestCase { } $0.nonce = 6 $0.specVersion = 9200 - $0.network = .polkadot + $0.network = CoinType.polkadot.ss58Prefix $0.transactionVersion = 12 $0.privateKey = key.data $0.stakingCall.chillAndUnbond = PolkadotStaking.ChillAndUnbond.with { diff --git a/tests/chains/Acala/TWAnyAddressTests.cpp b/tests/chains/Acala/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..47f9a823974 --- /dev/null +++ b/tests/chains/Acala/TWAnyAddressTests.cpp @@ -0,0 +1,49 @@ +// Copyright © 2017-2022 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 +#include +#include "HexCoding.h" +#include "Hash.h" +#include "PublicKey.h" +#include "Bech32Address.h" + +#include "TestUtilities.h" +#include + +namespace TW::Polkadot::tests { + +inline constexpr uint32_t acalaPrefix{10}; + +TEST(TWAcalaAnyAddress, IsValid) { + EXPECT_TRUE(TWAnyAddressIsValidSS58(STRING("212ywJGVK2Nxnt5bjKXVHi4YY7FCFd4rVvhyt95CjpeHGZee").get(), TWCoinTypePolkadot, acalaPrefix)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("212ywJGVK2Nxnt5bjKXVHi4YY7FCFd4rVvhyt95CjpeHGZee").get(), TWCoinTypePolkadot)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("212ywJGVK2Nxnt5bjKXVHi4YY7FCFd4rVvhyt95CjpeHGZee").get(), TWCoinTypeBitcoin)); + EXPECT_FALSE(TWAnyAddressIsValidSS58(STRING("15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu").get(), TWCoinTypePolkadot, acalaPrefix)); +} + +TEST(TWAcalaAnyAddress, createFromPubKeyAcala) { + const auto data = DATA("92fd9c237030356e26cfcc4568dc71055d5ec92dfe0ff903767e00611971bad3"); + const auto pubkey = TWPublicKeyCreateWithData(data.get(), TWPublicKeyTypeED25519); + const auto twAddress = TWAnyAddressCreateSS58WithPublicKey(pubkey, TWCoinTypePolkadot, acalaPrefix); + auto address = TWAnyAddressDescription(twAddress); + EXPECT_EQ("24CKv1LJ1T3U9ujCN63YzTPuQjcmURGA2xTjim98UKXxgNXT", *reinterpret_cast(address)); + TWStringDelete(address); + TWAnyAddressDelete(twAddress); + TWPublicKeyDelete(pubkey); +} + +TEST(TWAcalaAnyAddress, createFromStringAcala) { + const auto acalaAddress = STRING("24CKv1LJ1T3U9ujCN63YzTPuQjcmURGA2xTjim98UKXxgNXT"); + const auto anyAddr = TWAnyAddressCreateSS58(acalaAddress.get(), TWCoinTypePolkadot, acalaPrefix); + const auto addrDescription = TWAnyAddressDescription(anyAddr); + ASSERT_TRUE(TWAnyAddressIsValidSS58(addrDescription, TWCoinTypePolkadot, acalaPrefix)); + TWStringDelete(addrDescription); + TWAnyAddressDelete(anyAddr); + +} + +} diff --git a/tests/chains/Juno/TWAnyAddressTests.cpp b/tests/chains/Juno/TWAnyAddressTests.cpp index 2eddab2c3c7..491bcd20a78 100644 --- a/tests/chains/Juno/TWAnyAddressTests.cpp +++ b/tests/chains/Juno/TWAnyAddressTests.cpp @@ -10,6 +10,8 @@ #include "Bech32Address.h" #include "HexCoding.h" #include "Hash.h" +#include "PublicKey.h" +#include "Bech32Address.h" #include "TestUtilities.h" #include diff --git a/tests/chains/Kusama/SignerTests.cpp b/tests/chains/Kusama/SignerTests.cpp index 3af2df1c9ec..eac82e1a890 100644 --- a/tests/chains/Kusama/SignerTests.cpp +++ b/tests/chains/Kusama/SignerTests.cpp @@ -8,6 +8,7 @@ #include "Polkadot/Extrinsic.h" #include "Polkadot/SS58Address.h" #include "HexCoding.h" +#include "Coin.h" #include "PrivateKey.h" #include "PublicKey.h" #include "proto/Polkadot.pb.h" @@ -32,7 +33,7 @@ TEST(PolkadotSigner, SignTransferKSM) { input.set_nonce(0); input.set_spec_version(2019); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::KUSAMA); + input.set_network(ss58Prefix(TWCoinType::TWCoinTypeKusama)); input.set_transaction_version(2); auto balanceCall = input.mutable_balance_call(); diff --git a/tests/chains/Kusama/TWAnySignerTests.cpp b/tests/chains/Kusama/TWAnySignerTests.cpp index c23abdd9acb..60905a5633b 100644 --- a/tests/chains/Kusama/TWAnySignerTests.cpp +++ b/tests/chains/Kusama/TWAnySignerTests.cpp @@ -24,7 +24,7 @@ TEST(TWAnySignerKusama, Sign) { input.set_nonce(1); input.set_spec_version(2019); input.set_private_key(key.data(), key.size()); - input.set_network(Proto::Network::KUSAMA); + input.set_network(TWCoinTypeSS58Prefix(TWCoinTypeKusama)); input.set_transaction_version(2); auto balanceCall = input.mutable_balance_call(); diff --git a/tests/chains/Polkadot/SignerTests.cpp b/tests/chains/Polkadot/SignerTests.cpp index 17164a45ea4..c3ee02fe27b 100644 --- a/tests/chains/Polkadot/SignerTests.cpp +++ b/tests/chains/Polkadot/SignerTests.cpp @@ -12,6 +12,7 @@ #include "PrivateKey.h" #include "PublicKey.h" #include "proto/Polkadot.pb.h" +#include "Coin.h" #include "uint256.h" #include @@ -43,7 +44,7 @@ TEST(PolkadotSigner, SignTransfer_9fd062) { EXPECT_EQ(address.string(), addressThrow2); } input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(5); // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true @@ -78,7 +79,7 @@ TEST(PolkadotSigner, SignTransferDOT) { input.set_nonce(0); input.set_spec_version(17); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(3); auto& era = *input.mutable_era(); @@ -110,7 +111,7 @@ TEST(PolkadotSigner, SignTransfer_72dd5b) { input.set_nonce(1); input.set_spec_version(28); input.set_private_key(privateKeyIOS.bytes.data(), privateKeyIOS.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(6); auto& era = *input.mutable_era(); @@ -144,7 +145,7 @@ TEST(PolkadotSigner, SignBond_8da66d) { EXPECT_EQ(address.string(), addressThrow2); } input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(5); // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true @@ -173,7 +174,7 @@ TEST(PolkadotSigner, SignBondAndNominate_4955314_2) { input.set_nonce(4); input.set_spec_version(30); input.set_private_key(key.data(), key.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(7); auto stakingCall = input.mutable_staking_call(); @@ -198,7 +199,7 @@ TEST(PolkadotSigner, SignNominate_452522) { input.set_nonce(1); input.set_spec_version(26); input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(5); // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true @@ -226,7 +227,7 @@ TEST(PolkadotSigner, SignNominate2) { input.set_nonce(0); input.set_spec_version(17); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(3); auto stakingCall = input.mutable_staking_call(); @@ -253,7 +254,7 @@ TEST(PolkadotSigner, SignChill) { input.set_nonce(0); input.set_spec_version(17); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(3); auto stakingCall = input.mutable_staking_call(); @@ -272,7 +273,7 @@ TEST(PolkadotSigner, SignWithdraw) { input.set_nonce(0); input.set_spec_version(17); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(3); auto stakingCall = input.mutable_staking_call(); @@ -293,7 +294,7 @@ TEST(PolkadotSigner, SignUnbond_070957) { input.set_nonce(2); input.set_spec_version(26); input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(5); auto era = input.mutable_era(); @@ -319,7 +320,7 @@ TEST(PolkadotSigner, SignChillAndUnbond) { input.set_nonce(6); input.set_spec_version(9200); input.set_private_key(privateKeyPolkadot.bytes.data(), privateKeyPolkadot.bytes.size()); - input.set_network(Proto::Network::POLKADOT); + input.set_network(ss58Prefix(TWCoinTypePolkadot)); input.set_transaction_version(12); auto era = input.mutable_era(); @@ -336,4 +337,4 @@ TEST(PolkadotSigner, SignChillAndUnbond) { ASSERT_EQ(hex(output.encoded()), "d10184008361bd08ddca5fda28b5e2aa84dc2621de566e23e089e555a42194c3eaf2da7900c891ba102db672e378945d74cf7f399226a76b43cab502436971599255451597fc2599902e4b62c7ce85ecc3f653c693fef3232be620984b5bb5bcecbbd7b209d50318001a02080706070207004d446617"); } -} // namespace TW::Polkadot::tests +} // namespace Polkadot::tests From a21834107b5646907961dd7680f0068f313feca0 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 26 Oct 2022 06:59:29 +0200 Subject: [PATCH 026/426] [Elrond]: add elrond staking unit tests (#2675) --- .../blockchains/elrond/TestElrondSigner.kt | 66 +++++++ swift/Tests/Blockchains/ElrondTests.swift | 58 ++++++ tests/chains/Elrond/SignerTests.cpp | 186 ++++++++++++++++++ 3 files changed, 310 insertions(+) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt index a99a949bdb4..44a9e16b2a3 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt @@ -57,6 +57,72 @@ class TestElrondSigner { assertEquals("""{"nonce":7,"value":"0","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"$expectedSignature"}""", output.encoded) } + @Test + fun signGenericActionUndelegate() { + // Successfully broadcasted https://explorer.elrond.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 + val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) + + val accounts = Elrond.Accounts.newBuilder() + .setSenderNonce(6) + .setSender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa") + .setReceiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r") + .build() + + val genericAction = Elrond.GenericAction.newBuilder() + .setAccounts(accounts) + .setValue("0") + .setData("unDelegate@0de0b6b3a7640000") + .setVersion(1) + .build() + + val signingInput = Elrond.SigningInput.newBuilder() + .setGenericAction(genericAction) + .setGasPrice(1000000000) + .setGasLimit(12000000) + .setChainId("1") + .setPrivateKey(privateKey) + .build() + + val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val expectedSignature = "89f9683af92f7b835bff4e1d0dbfcff5245b3367df4d23538eb799e0ad0a90be29ac3bd3598ce55b35b35ebef68bfa5738eed39fd01adc33476f65bd1b966e0b" + + assertEquals(expectedSignature, output.signature) + assertEquals("""{"nonce":6,"value":"0","receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r","sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa","gasPrice":1000000000,"gasLimit":12000000,"data":"dW5EZWxlZ2F0ZUAwZGUwYjZiM2E3NjQwMDAw","chainID":"1","version":1,"signature":"$expectedSignature"}""", output.encoded) + } + + @Test + fun signGenericActionDelegate() { + // Successfully broadcasted https://explorer.elrond.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 + val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) + + val accounts = Elrond.Accounts.newBuilder() + .setSenderNonce(1) + .setSender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa") + .setReceiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r") + .build() + + val genericAction = Elrond.GenericAction.newBuilder() + .setAccounts(accounts) + .setValue("1") + .setData("delegate") + .setVersion(1) + .build() + + val signingInput = Elrond.SigningInput.newBuilder() + .setGenericAction(genericAction) + .setGasPrice(1000000000) + .setGasLimit(12000000) + .setChainId("1") + .setPrivateKey(privateKey) + .build() + + val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val expectedSignature = "3b9164d47a4e3c0330ae387cd29ba6391f9295acf5e43a16a4a2611645e66e5fa46bf22294ca68fe1948adf45cec8cb47b8792afcdb248bd9adec7c6e6c27108" + + assertEquals(expectedSignature, output.signature) + assertEquals("""{"nonce":1,"value":"1","receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r","sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa","gasPrice":1000000000,"gasLimit":12000000,"data":"ZGVsZWdhdGU=","chainID":"1","version":1,"signature":"$expectedSignature"}""", output.encoded) + } + @Test fun signEGLDTransfer() { val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) diff --git a/swift/Tests/Blockchains/ElrondTests.swift b/swift/Tests/Blockchains/ElrondTests.swift index 06718ed42a3..beee15a7a26 100644 --- a/swift/Tests/Blockchains/ElrondTests.swift +++ b/swift/Tests/Blockchains/ElrondTests.swift @@ -51,6 +51,64 @@ class ElrondTests: XCTestCase { XCTAssertEqual(output.signature, expectedSignature) XCTAssertEqual(output.encoded, expectedEncoded) } + + func testSignGenericActionUndelegate() { + // Successfully broadcasted https://explorer.elrond.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 + let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + + let input = ElrondSigningInput.with { + $0.genericAction = ElrondGenericAction.with { + $0.accounts = ElrondAccounts.with { + $0.senderNonce = 6 + $0.sender = "erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa" + $0.receiver = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r" + } + $0.value = "0" + $0.data = "unDelegate@0de0b6b3a7640000" + $0.version = 1 + } + $0.gasPrice = 1000000000 + $0.gasLimit = 12000000 + $0.chainID = "1" + $0.privateKey = privateKey.data + } + + let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let expectedSignature = "89f9683af92f7b835bff4e1d0dbfcff5245b3367df4d23538eb799e0ad0a90be29ac3bd3598ce55b35b35ebef68bfa5738eed39fd01adc33476f65bd1b966e0b" + let expectedEncoded = #"{"nonce":6,"value":"0","receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r","sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa","gasPrice":1000000000,"gasLimit":12000000,"data":"dW5EZWxlZ2F0ZUAwZGUwYjZiM2E3NjQwMDAw","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# + + XCTAssertEqual(output.signature, expectedSignature) + XCTAssertEqual(output.encoded, expectedEncoded) + } + + func testSignGenericActionDelegate() { + // Successfully broadcasted https://explorer.elrond.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 + let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + + let input = ElrondSigningInput.with { + $0.genericAction = ElrondGenericAction.with { + $0.accounts = ElrondAccounts.with { + $0.senderNonce = 1 + $0.sender = "erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa" + $0.receiver = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r" + } + $0.value = "1" + $0.data = "delegate" + $0.version = 1 + } + $0.gasPrice = 1000000000 + $0.gasLimit = 12000000 + $0.chainID = "1" + $0.privateKey = privateKey.data + } + + let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let expectedSignature = "3b9164d47a4e3c0330ae387cd29ba6391f9295acf5e43a16a4a2611645e66e5fa46bf22294ca68fe1948adf45cec8cb47b8792afcdb248bd9adec7c6e6c27108" + let expectedEncoded = #"{"nonce":1,"value":"1","receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r","sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa","gasPrice":1000000000,"gasLimit":12000000,"data":"ZGVsZWdhdGU=","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# + + XCTAssertEqual(output.signature, expectedSignature) + XCTAssertEqual(output.encoded, expectedEncoded) + } func testSignEGLDTransfer() { let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! diff --git a/tests/chains/Elrond/SignerTests.cpp b/tests/chains/Elrond/SignerTests.cpp index aeaa8455a69..f2348dbe155 100644 --- a/tests/chains/Elrond/SignerTests.cpp +++ b/tests/chains/Elrond/SignerTests.cpp @@ -6,13 +6,16 @@ #include "boost/format.hpp" #include +#include #include "Elrond/Address.h" #include "Elrond/Signer.h" +#include "Elrond/Codec.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" #include "TestAccounts.h" +#include "TestUtilities.h" using namespace TW; @@ -43,6 +46,189 @@ TEST(ElrondSigner, SignGenericAction) { ASSERT_EQ(expectedSignature, signature); } +TEST(ElrondSigner, SignGenericActionUnDelegate) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(6); + input.mutable_generic_action()->mutable_accounts()->set_sender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa"); + input.mutable_generic_action()->mutable_accounts()->set_receiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r"); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data("unDelegate@" + TW::Elrond::Codec::encodeBigInt("1000000000000000000")); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(12000000); + input.set_chain_id("1"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "89f9683af92f7b835bff4e1d0dbfcff5245b3367df4d23538eb799e0ad0a90be29ac3bd3598ce55b35b35ebef68bfa5738eed39fd01adc33476f65bd1b966e0b"; + nlohmann::json expected = R"( + { + "chainID":"1", + "data":"dW5EZWxlZ2F0ZUAwZGUwYjZiM2E3NjQwMDAw", + "gasLimit":12000000, + "gasPrice":1000000000, + "nonce":6, + "receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r", + "sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa", + "signature":"89f9683af92f7b835bff4e1d0dbfcff5245b3367df4d23538eb799e0ad0a90be29ac3bd3598ce55b35b35ebef68bfa5738eed39fd01adc33476f65bd1b966e0b", + "value":"0", + "version":1 + })"_json; + assertJSONEqual(expected, nlohmann::json::parse(encoded)); + ASSERT_EQ(expectedSignature, signature); + // Successfully broadcasted https://explorer.elrond.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 +} + +TEST(ElrondSigner, SignGenericActionRedelegateRewards) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(7); + input.mutable_generic_action()->mutable_accounts()->set_sender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa"); + input.mutable_generic_action()->mutable_accounts()->set_receiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r"); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data("reDelegateRewards"); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(12000000); + input.set_chain_id("1"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "fc0238d41e4d02a24ac8a502cc3d59e406258b5c186c883e2e9aeffa859a818f5317bf22c9bc6d3838c64529953a46c1d4aabc485f96675a4c4dd642f5f50402"; + nlohmann::json expected = R"( + { + "chainID":"1", + "data":"cmVEZWxlZ2F0ZVJld2FyZHM=", + "gasLimit":12000000, + "gasPrice":1000000000, + "nonce":7, + "receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r", + "sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa", + "signature":"fc0238d41e4d02a24ac8a502cc3d59e406258b5c186c883e2e9aeffa859a818f5317bf22c9bc6d3838c64529953a46c1d4aabc485f96675a4c4dd642f5f50402", + "value":"0", + "version":1 + })"_json; + assertJSONEqual(expected, nlohmann::json::parse(encoded)); + ASSERT_EQ(expectedSignature, signature); +} + +TEST(ElrondSigner, SignGenericActionClaimRewards) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(7); + input.mutable_generic_action()->mutable_accounts()->set_sender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa"); + input.mutable_generic_action()->mutable_accounts()->set_receiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r"); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data("claimRewards"); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(6000000); + input.set_chain_id("1"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "c453652214d428045721ad5560194a699ce4194ba7edcbdc1c4f5d1e9a605b82bb0a0fd7dba708322b62518d5d5af3e7380efab0804ac00cdafe7598e7498900"; + nlohmann::json expected = R"( + { + "chainID":"1", + "data":"Y2xhaW1SZXdhcmRz", + "gasLimit":6000000, + "gasPrice":1000000000, + "nonce":7, + "receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r", + "sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa", + "signature":"c453652214d428045721ad5560194a699ce4194ba7edcbdc1c4f5d1e9a605b82bb0a0fd7dba708322b62518d5d5af3e7380efab0804ac00cdafe7598e7498900", + "value":"0", + "version":1 + })"_json; + assertJSONEqual(expected, nlohmann::json::parse(encoded)); + ASSERT_EQ(expectedSignature, signature); +} + +TEST(ElrondSigner, SignGenericActionWithdrawStake) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(7); + input.mutable_generic_action()->mutable_accounts()->set_sender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa"); + input.mutable_generic_action()->mutable_accounts()->set_receiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r"); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data("withdraw"); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(12000000); + input.set_chain_id("1"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "a2a17498e78e29082c433c009895bd949fc68b2222620d8f5350f821350cde390c15ffe00df4f0e84a074abd892331b79503bf458a35cb90333d1350553d9302"; + nlohmann::json expected = R"( + { + "chainID":"1", + "data":"d2l0aGRyYXc=", + "gasLimit":12000000, + "gasPrice":1000000000, + "nonce":7, + "receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r", + "sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa", + "signature":"a2a17498e78e29082c433c009895bd949fc68b2222620d8f5350f821350cde390c15ffe00df4f0e84a074abd892331b79503bf458a35cb90333d1350553d9302", + "value":"0", + "version":1 + })"_json; + assertJSONEqual(expected, nlohmann::json::parse(encoded)); + ASSERT_EQ(expectedSignature, signature); + // Need to wait 9 days for broadcasting +} + +TEST(ElrondSigner, SignGenericActionDelegate) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(1); + input.mutable_generic_action()->mutable_accounts()->set_sender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa"); + input.mutable_generic_action()->mutable_accounts()->set_receiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r"); + input.mutable_generic_action()->set_value("1"); + input.mutable_generic_action()->set_data("delegate"); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(12000000); + input.set_chain_id("1"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "3b9164d47a4e3c0330ae387cd29ba6391f9295acf5e43a16a4a2611645e66e5fa46bf22294ca68fe1948adf45cec8cb47b8792afcdb248bd9adec7c6e6c27108"; + nlohmann::json expected = R"( + { + "chainID":"1", + "data":"ZGVsZWdhdGU=", + "gasLimit":12000000, + "gasPrice":1000000000, + "nonce":1, + "receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r", + "sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa", + "signature":"3b9164d47a4e3c0330ae387cd29ba6391f9295acf5e43a16a4a2611645e66e5fa46bf22294ca68fe1948adf45cec8cb47b8792afcdb248bd9adec7c6e6c27108", + "value":"1", + "version":1 + })"_json; + assertJSONEqual(expected, nlohmann::json::parse(encoded)); + ASSERT_EQ(expectedSignature, signature); + // Successfully broadcasted https://explorer.elrond.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 +} + TEST(ElrondSigner, SignGenericActionJSON) { // Shuffle some fields, assume arbitrary order in the input auto input = (boost::format(R"({"genericAction" : {"accounts": {"senderNonce": 7, "receiver": "%1%", "sender": "%2%"}, "data": "foo", "value": "0", "version": 1}, "gasPrice": 1000000000, "gasLimit": 50000, "chainId": "1"})") % BOB_BECH32 % ALICE_BECH32).str(); From f7217bd0c9ccdba7d6699cfc67887ece4465e1a8 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Wed, 26 Oct 2022 07:00:50 +0200 Subject: [PATCH 027/426] Rust sample app (#2660) --- .github/workflows/linux-sampleapp-ci.yml | 8 +- samples/rust/.gitignore | 6 + samples/rust/Cargo.lock | 333 +++++++++++++++++++++++ samples/rust/Cargo.toml | 16 ++ samples/rust/README.md | 32 +++ samples/rust/src/build.rs | 46 ++++ samples/rust/src/main.rs | 76 ++++++ samples/rust/src/walletcore_extra.rs | 68 +++++ samples/rust/src/walletcore_iface.rs | 169 ++++++++++++ 9 files changed, 753 insertions(+), 1 deletion(-) create mode 100644 samples/rust/.gitignore create mode 100644 samples/rust/Cargo.lock create mode 100644 samples/rust/Cargo.toml create mode 100644 samples/rust/README.md create mode 100644 samples/rust/src/build.rs create mode 100644 samples/rust/src/main.rs create mode 100644 samples/rust/src/walletcore_extra.rs create mode 100644 samples/rust/src/walletcore_iface.rs diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index 3d16471abbd..67bbec223ee 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - name: Install system dependencies run: | - sudo apt-get update && sudo apt-get install ninja-build llvm-11 libboost-all-dev clang-11 --fix-missing + sudo apt-get update && sudo apt-get install ninja-build llvm-11 libboost-all-dev clang-11 rustc --fix-missing - name: Cache internal dependencies id: internal_cache uses: actions/cache@v1.1.2 @@ -88,3 +88,9 @@ jobs: env: CC: /usr/bin/clang CXX: /usr/bin/clang++ + - name: Build and run Rust sample app + run: | + cd samples/rust + rustc --version + cargo build + cargo run diff --git a/samples/rust/.gitignore b/samples/rust/.gitignore new file mode 100644 index 00000000000..4ff40df7737 --- /dev/null +++ b/samples/rust/.gitignore @@ -0,0 +1,6 @@ +# build directory +target + +# Generated proto files +src/wc_proto/*.rs + diff --git a/samples/rust/Cargo.lock b/samples/rust/Cargo.lock new file mode 100644 index 00000000000..029c2e3f6e7 --- /dev/null +++ b/samples/rust/Cargo.lock @@ -0,0 +1,333 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.135" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "protobuf" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror", +] + +[[package]] +name = "protobuf-codegen" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901" +dependencies = [ + "anyhow", + "once_cell", + "protobuf", + "protobuf-parse", + "regex", + "tempfile", + "thiserror", +] + +[[package]] +name = "protobuf-parse" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49" +dependencies = [ + "anyhow", + "indexmap", + "log", + "protobuf", + "protobuf-support", + "tempfile", + "thiserror", + "which", +] + +[[package]] +name = "protobuf-support" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +dependencies = [ + "thiserror", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustsample" +version = "1.0.0" +dependencies = [ + "cc", + "hex", + "libc", + "pkg-config", + "protobuf", + "protobuf-codegen", +] + +[[package]] +name = "syn" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "which" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/samples/rust/Cargo.toml b/samples/rust/Cargo.toml new file mode 100644 index 00000000000..4f27795f435 --- /dev/null +++ b/samples/rust/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rustsample" +version = "1.0.0" +edition = "2021" +build = "src/build.rs" + +[dependencies] +hex = "0.4.3" +libc = "0.2" +protobuf = "3.2.0" + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +pkg-config = "0.3" +protobuf = "3.2.0" +protobuf-codegen = "3.2.0" diff --git a/samples/rust/README.md b/samples/rust/README.md new file mode 100644 index 00000000000..535990b95c1 --- /dev/null +++ b/samples/rust/README.md @@ -0,0 +1,32 @@ +# Rust Sample Application + +A simple Rust application to how to link and use wallet-core from Rust. + +## Prerequisites + +- Compiled `wallet-core` library +- Rust (`rustc`, `cargo`) + +## Building and Running + +- In the `build.rs` file set the path to the wallet-core folder (by default simply `../..`). + +``` +cargo build +cargo run +``` + +## Some Details + +- The app links with the wallet-core library (C/C++). +- The `walletcore_iface.rs` file contains the interface definitions in Rust. +- Links with `TrustWalletCore`, `TrezorCrypto`, `protobuf`, and the platform libc (`c++` or `stdc++`). Build/link paramaters are in `build.rs`. +- Rust proto files are created during the build process, from the `.proto` files in wallet-core, +into subfolder `src/wc_proto` +(see `build.rs`). +- Notable dependecies: + -- `protobuf` + -- `lib` for C linking + -- `hex` + -- `protobuf-codegen` for generating protobuf source files + diff --git a/samples/rust/src/build.rs b/samples/rust/src/build.rs new file mode 100644 index 00000000000..cb96ab2c9c9 --- /dev/null +++ b/samples/rust/src/build.rs @@ -0,0 +1,46 @@ +// Copyright © 2017-2022 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. + +use std::fs; +use std::path::Path; + +// Wallet-core project folder, with sources (proto) and build library binaries +static WALLET_CORE_PROJECT_DIR: &str = "../.."; + +// libs to link with, in reverse dependency order +static LIBS: [&str; 3] = ["TrustWalletCore", "TrezorCrypto", "protobuf"]; + +fn main() { + // Generate protobuf interface files + let proto_src: String = WALLET_CORE_PROJECT_DIR.to_string() + "/src/proto"; + let out_dir: &str = "src/wc_proto"; + + let _create_folder_res = fs::create_dir_all(out_dir); + protobuf_codegen::Codegen::new() + //.protoc() // use `protoc` parser, optional. + .protoc_path(Path::new(&(WALLET_CORE_PROJECT_DIR.to_string() + "/build/local/bin/protoc"))) + .out_dir(out_dir) + .input(proto_src.to_string() + "/Common.proto") + .input(proto_src.to_string() + "/Bitcoin.proto") + .input(proto_src.to_string() + "/Ethereum.proto") + .include(proto_src) + .run() + .expect("Codegen failed."); + println!("Protobuf codegen to {} ready", out_dir); + + println!("cargo:rustc-link-search=native={}/build", WALLET_CORE_PROJECT_DIR); + println!("cargo:rustc-link-search=native={}/build/trezor-crypto", WALLET_CORE_PROJECT_DIR); + + // Libraries; order matters + for i in 0..LIBS.len() { + println!("cargo:rustc-link-lib={}", LIBS[i]); + } + if cfg!(target_os = "macos") { + println!("cargo:rustc-link-lib=c++"); + } else { // "linux", etc + println!("cargo:rustc-link-lib=stdc++"); + } +} diff --git a/samples/rust/src/main.rs b/samples/rust/src/main.rs new file mode 100644 index 00000000000..a2228f5fcb8 --- /dev/null +++ b/samples/rust/src/main.rs @@ -0,0 +1,76 @@ +// Copyright © 2017-2022 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. + +pub mod walletcore_iface; +pub mod walletcore_extra; +pub mod wc_proto; + +use crate::walletcore_iface::*; +use crate::walletcore_extra::*; +use crate::wc_proto::Ethereum; +use protobuf::Message; +use hex::ToHex; + +// returns private key +fn get_wallet_info(coin: u32, coin_name: &str, wallet: &HDWallet) -> TWData { + println!("coin {}:", coin_name); + let address = hd_wallet_get_address_for_coin(wallet, coin); + println!(" address: {}", address.to_string()); + + let priv_key = hd_wallet_get_key_for_coin(wallet, coin); + let priv_key_data = private_key_data(&priv_key); + println!(" privkey: {}", priv_key_data.to_hex()); + + let pub_key = private_key_get_public_key_secp256k1(&priv_key, true); + let pub_key_data = public_key_data(&pub_key); + println!(" pubkey: {}", pub_key_data.to_hex()); + + return priv_key_data; +} + +fn main() { + println!("=== Calling wallet-core from Rust"); + + let mnemonic = "confirm bleak useless tail chalk destroy horn step bulb genuine attract split"; + println!("mnemonic is valid: {}", mnemonic_is_valid(&TWString::from_str(mnemonic))); + + let mnemonic_tw = TWString::from_str(mnemonic); + if !mnemonic_is_valid(&mnemonic_tw) { + println!("Mnemonic is invalid! {}", mnemonic); + return; + } + println!("wallet created, mnemonic {}", mnemonic); + let wallet = hd_wallet_create_with_mnemonic(&mnemonic_tw, &TWString::from_str("")); + + get_wallet_info(0, "bitcoin", &wallet); + + let eth_pk = get_wallet_info(60, "ethereum", &wallet); + + // Ethereum transaction + println!("Signing Ethereum transaction"); + let mut signing_input = Ethereum::SigningInput::new(); + signing_input.chain_id = vec!(4); + signing_input.nonce = vec!(0); + signing_input.tx_mode = ::protobuf::EnumOrUnknown::new(Ethereum::TransactionMode::Legacy); + signing_input.gas_price = hex::decode("00174876E800").unwrap(); // 100000000000 100 gwei + signing_input.gas_limit = hex::decode("005208").unwrap(); // 21000 + signing_input.to_address = "0xE9B511C0753649E5F3E78Ed8AdBEE92d0d2Db384".to_string(); + signing_input.private_key = eth_pk.to_vec(); + let mut transfer = Ethereum::transaction::Transfer::new(); + transfer.amount = hex::decode("002386F26FC10000").unwrap(); // 10000000000000000 0.01 + let mut transaction = Ethereum::Transaction::new(); + transaction.transaction_oneof = Some(Ethereum::transaction::Transaction_oneof::Transfer(transfer)); + signing_input.transaction = ::protobuf::MessageField::some(transaction); + + let input_ser = signing_input.write_to_bytes().unwrap(); + let input_ser_data = TWData::from_vec(&input_ser); + + let output_ser_data = any_signer_sign(&input_ser_data, 60); + + let outputp: Ethereum::SigningOutput = protobuf::Message::parse_from_bytes(&output_ser_data.to_vec()).unwrap(); + let x: Vec = outputp.encoded; + println!(" signed tx: {}", x.encode_hex::()); +} diff --git a/samples/rust/src/walletcore_extra.rs b/samples/rust/src/walletcore_extra.rs new file mode 100644 index 00000000000..6f3b1c49e98 --- /dev/null +++ b/samples/rust/src/walletcore_extra.rs @@ -0,0 +1,68 @@ +// Copyright © 2017-2022 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. + +// Extra type helpers, traits for wallet-core interfaces + +use crate::walletcore_iface::*; +use hex::ToHex; +use std::ffi::CString; +use std::ffi::CStr; + + +// Some traits +pub trait FromString { + fn from_str(s: &str) -> Self; +} + +pub trait ToVec { + fn to_vec(self: &Self) -> Vec; +} + +pub trait FromVec { + fn from_vec(vec: &Vec) -> Self; +} + +pub trait ToHexString { + fn to_hex(self: &Self) -> String; +} + + +// TWString trait implementations +impl FromString for TWString { + fn from_str(s: &str) -> Self { + let cstring = CString::new(s).unwrap(); + tw_string_create_with_utf8_bytes(cstring.as_ptr()) + } +} + +impl ToString for TWString { + fn to_string(&self) -> String { + let s1 = tw_string_utf8_bytes(&self); + let c_str: &CStr = unsafe { CStr::from_ptr(s1) }; + let str_slice: &str = c_str.to_str().unwrap(); + str_slice.to_owned() + } +} + + +// TWData trait implementations +impl ToVec for TWData { + fn to_vec(&self) -> Vec { + tw_data_bytes(&self) + } +} + +impl FromVec for TWData { + fn from_vec(v: &Vec) -> Self { + tw_data_create_with_bytes(v) + } +} + +impl ToHexString for TWData { + fn to_hex(&self) -> String { + self.to_vec().encode_hex::() + } +} diff --git a/samples/rust/src/walletcore_iface.rs b/samples/rust/src/walletcore_iface.rs new file mode 100644 index 00000000000..5e3a71f8f15 --- /dev/null +++ b/samples/rust/src/walletcore_iface.rs @@ -0,0 +1,169 @@ +// Copyright © 2017-2022 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. + +// Rust interfaces to wallet-core +// Could be auto-generated + +use libc::c_char; + +extern "C" { + fn TWStringCreateWithUTF8Bytes(bytes: *const c_char) -> *const u8; + fn TWStringDelete(twstring: *const u8); + fn TWStringUTF8Bytes(twstring: *const u8) -> *const c_char; +} + +pub struct TWString { + wrapped: *const u8 +} + +pub fn tw_string_create_with_utf8_bytes(bytes: *const c_char) -> TWString { + let ptr = unsafe { TWStringCreateWithUTF8Bytes(bytes) }; + TWString { wrapped: ptr } +} + +pub fn tw_string_utf8_bytes(twstring: &TWString) -> *const c_char { + unsafe { TWStringUTF8Bytes(twstring.wrapped) } +} + +impl Drop for TWString { + fn drop(&mut self) { + unsafe { TWStringDelete(self.wrapped) }; + } +} + + +extern "C" { + fn TWDataCreateWithBytes(bytes: *const u8, size: usize) -> *const u8; + fn TWDataDelete(data: *const u8); + fn TWDataSize(data: *const u8) -> usize; + fn TWDataBytes(data: *const u8) -> *const u8; +} + +pub struct TWData { + wrapped: *const u8 +} + +pub fn tw_data_create_with_bytes(bytes: &Vec) -> TWData { + let ptr = unsafe { TWDataCreateWithBytes(bytes.as_ptr(), bytes.len()) }; + TWData { wrapped: ptr } +} + +pub fn tw_data_size(data: &TWData) -> usize { + unsafe { TWDataSize(data.wrapped) } +} + +pub fn tw_data_bytes(data: &TWData) -> Vec { + let size = tw_data_size(data); + let ptr = unsafe { TWDataBytes(data.wrapped) }; + let slice: &[u8] = unsafe { std::slice::from_raw_parts(ptr, size) }; + slice.to_vec() +} + +impl Drop for TWData { + fn drop(&mut self) { + unsafe { TWDataDelete(self.wrapped) }; + } +} + + +extern "C" { + fn TWPrivateKeyData(private_key: *const u8) -> *const u8; + fn TWPrivateKeyGetPublicKeySecp256k1(private_key: *const u8, compressed: bool) -> *const u8; + fn TWPrivateKeyDelete(private_key: *const u8); +} + +pub struct PrivateKey { + wrapped: *const u8 +} + +pub fn private_key_data(private_key: &PrivateKey) -> TWData { + let ptr = unsafe { TWPrivateKeyData(private_key.wrapped) }; + TWData { wrapped: ptr } +} + +pub fn private_key_get_public_key_secp256k1(private_key: &PrivateKey, compressed: bool) -> PublicKey { + let ptr = unsafe { TWPrivateKeyGetPublicKeySecp256k1(private_key.wrapped, compressed) }; + PublicKey { wrapped: ptr } +} + +impl Drop for PrivateKey { + fn drop(&mut self) { + unsafe { TWPrivateKeyDelete(self.wrapped) }; + } +} + + +extern "C" { + fn TWPublicKeyData(public_key: *const u8) -> *const u8; + fn TWPublicKeyDelete(public_key: *const u8); +} + +pub struct PublicKey { + wrapped: *const u8 +} + +pub fn public_key_data(public_key: &PublicKey) -> TWData { + let ptr = unsafe { TWPublicKeyData(public_key.wrapped) }; + TWData { wrapped: ptr } +} + +impl Drop for PublicKey { + fn drop(&mut self) { + unsafe { TWPublicKeyDelete(self.wrapped) }; + } +} + + +extern "C" { + fn TWHDWalletCreateWithMnemonic(mnemonic: *const u8, passphrase: *const u8) -> *const u8; + fn TWHDWalletDelete(wallet: *const u8); + fn TWHDWalletGetAddressForCoin(wallet: *const u8, coin: u32) -> *const u8; + fn TWHDWalletGetKeyForCoin(wallet: *const u8, coin: u32) -> *const u8; +} + +pub struct HDWallet { + wrapped: *const u8 +} + +pub fn hd_wallet_create_with_mnemonic(mnemonic: &TWString, passphrase: &TWString) -> HDWallet { + let ptr = unsafe { TWHDWalletCreateWithMnemonic(mnemonic.wrapped, passphrase.wrapped) }; + HDWallet { wrapped: ptr } +} + +pub fn hd_wallet_get_address_for_coin(wallet: &HDWallet, coin: u32) -> TWString { + let ptr = unsafe { TWHDWalletGetAddressForCoin(wallet.wrapped, coin) }; + TWString { wrapped: ptr } +} + +pub fn hd_wallet_get_key_for_coin(wallet: &HDWallet, coin: u32) -> PrivateKey { + let ptr = unsafe { TWHDWalletGetKeyForCoin(wallet.wrapped, coin) }; + PrivateKey { wrapped: ptr } +} + +impl Drop for HDWallet { + fn drop(&mut self) { + unsafe { TWHDWalletDelete(self.wrapped) }; + } +} + + +extern "C" { + fn TWAnySignerSign(input: *const u8, coin: u32) -> *const u8; +} + +pub fn any_signer_sign(input: &TWData, coin: u32) -> TWData { + let ptr = unsafe { TWAnySignerSign(input.wrapped, coin) }; + TWData { wrapped: ptr } +} + + +extern "C" { + fn TWMnemonicIsValid(mnemonic: *const u8) -> bool; +} + +pub fn mnemonic_is_valid(mnemonic: &TWString) -> bool { + unsafe { TWMnemonicIsValid(mnemonic.wrapped) } +} From d9c02ce4678a92179e7b38d3d8c933b71916d06b Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 26 Oct 2022 18:35:36 +0200 Subject: [PATCH 028/426] [CodeQuality]: Enable sonarcloud on CI run (#2676) --- .github/workflows/linux-ci-sonarcloud.yml | 49 +++++++++++++++++++++++ sonar-project.properties | 7 ++++ src/HDWallet.cpp | 2 +- src/Mnemonic.cpp | 2 +- src/Result.h | 6 +-- src/interface/TWTransactionCompiler.cpp | 12 +++--- tools/sonar-scanner.properties | 9 +++++ tools/sonarcloud-analysis | 9 +++++ 8 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/linux-ci-sonarcloud.yml create mode 100644 sonar-project.properties create mode 100644 tools/sonar-scanner.properties create mode 100755 tools/sonarcloud-analysis diff --git a/.github/workflows/linux-ci-sonarcloud.yml b/.github/workflows/linux-ci-sonarcloud.yml new file mode 100644 index 00000000000..fc69a791607 --- /dev/null +++ b/.github/workflows/linux-ci-sonarcloud.yml @@ -0,0 +1,49 @@ +name: Linux CI SonarCloud + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v2 + - name: Install system dependencies + run: | + # build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake + sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev --fix-missing + - name: Cache internal dependencies + id: internal_cache + uses: actions/cache@v1.1.2 + with: + path: build/local + key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + - name: Install internal dependencies + run: | + tools/install-dependencies + env: + CC: /usr/bin/clang + CXX: /usr/bin/clang++ + if: steps.internal_cache.outputs.cache-hit != 'true' + - name: Code generation + run: | + tools/generate-files + env: + CC: /usr/bin/clang + CXX: /usr/bin/clang++ + - name: CMake (coverage/clang-tidy/clang-asan) + run: | + cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -DTW_CODE_COVERAGE=ON -DTW_ENABLE_CLANG_TIDY=ON -DTW_CLANG_ASAN=ON -GNinja + cat build/compile_commands.json + env: + CC: /usr/bin/clang + CXX: /usr/bin/clang++ + - name: SonarCloud Scan + run: | + ./tools/sonarcloud-analysis + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000000..ec607dbae00 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.organization=trustwallet +sonar.projectKey=TrustWallet_wallet-core +sonar.cfamily.compile-commands=build/compile_commands.json +sonar.cfamily.reportingCppStandardOverride=c++20 +sonar.cfamily.cache.enabled=false +sonar.lang.patterns.cpp=**/*.cc,**/*.cpp,**/*.cxx,**/*.c++,**/*.hh,**/*.hpp,**/*.hxx,**/*.h++,**/*.ipp,**/*.h +sonar.lang.patterns.c=**/*.c diff --git a/src/HDWallet.cpp b/src/HDWallet.cpp index 47e84bdec75..db73dc18f9b 100644 --- a/src/HDWallet.cpp +++ b/src/HDWallet.cpp @@ -283,7 +283,7 @@ std::string serialize(const HDNode* node, uint32_t fingerprint, uint32_t version bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher, HDNode* node) { TW::memzero(node); const char* curveNameStr = curveName(curve); - if (curveNameStr == nullptr || ::strlen(curveNameStr) == 0) { + if (curveNameStr == nullptr || std::string(curveNameStr).empty()) { return false; } node->curve = get_curve_by_name(curveNameStr); diff --git a/src/Mnemonic.cpp b/src/Mnemonic.cpp index 1bff4d76ea7..1ed92d61361 100644 --- a/src/Mnemonic.cpp +++ b/src/Mnemonic.cpp @@ -32,7 +32,7 @@ bool Mnemonic::isValidWord(const std::string& word) { // (i.e., no early exit on match) auto found = false; for (const char* const* w = mnemonicWordlist(); *w != nullptr; ++w) { - if (strlen(*w) == len && strncmp(*w, wordC, len) == 0) { + if (std::string(*w).size() == len && strncmp(*w, wordC, len) == 0) { found = true; } } diff --git a/src/Result.h b/src/Result.h index 1ce611525a1..1f4991c3cc4 100644 --- a/src/Result.h +++ b/src/Result.h @@ -122,10 +122,10 @@ struct Result { E error() const { return get(); } /// Returns a new success result with the given payloadd. - static Result success(T&& val) { return Result(Types::Success(std::forward(val))); } + static Result success(T&& val) { return Result(Types::Success(std::move(val))); } /// Returns a new failure result with the given error. - static Result failure(E&& val) { return Result(Types::Failure(std::forward(val))); } + static Result failure(E&& val) { return Result(Types::Failure(std::move(val))); } static Result failure(E& val) { return Result(Types::Failure(val)); } @@ -171,7 +171,7 @@ struct Result { /// Returns a new failure result with the given error. static Result failure(E&& val) { - return Result(Types::Failure(std::forward(val))); + return Result(Types::Failure(std::move(val))); } operator bool() const { return success_; } diff --git a/src/interface/TWTransactionCompiler.cpp b/src/interface/TWTransactionCompiler.cpp index bd8b6bcf051..0b5b45e52a1 100644 --- a/src/interface/TWTransactionCompiler.cpp +++ b/src/interface/TWTransactionCompiler.cpp @@ -31,13 +31,15 @@ TWData *_Nonnull TWTransactionCompilerBuildInput(enum TWCoinType coinType, TWStr return TWDataCreateWithBytes(result.data(), result.size()); } -std::vector createFromTWDataVector(const struct TWDataVector* _Nonnull dataVector) { +static std::vector createFromTWDataVector(const struct TWDataVector* _Nonnull dataVector) { std::vector ret; const auto n = TWDataVectorSize(dataVector); - for (auto i = 0ul; i < n; ++i) { - auto elem = TWDataVectorGet(dataVector, i); - ret.push_back(*(static_cast(elem))); - TWDataDelete(elem); + for (auto i = 0uL; i < n; ++i) { + const auto* const elem = TWDataVectorGet(dataVector, i); + if (const auto* const data = reinterpret_cast(elem); data) { + ret.emplace_back(*data); + TWDataDelete(elem); + } } return ret; } diff --git a/tools/sonar-scanner.properties b/tools/sonar-scanner.properties new file mode 100644 index 00000000000..08eb8f97091 --- /dev/null +++ b/tools/sonar-scanner.properties @@ -0,0 +1,9 @@ +#Configure here general information about the environment, such as SonarQube server connection details for example +#No information about specific project should appear here + +#----- Default SonarQube server +sonar.host.url=https://sonarcloud.io/ + +#----- Default source code encoding +#sonar.sourceEncoding=UTF-8 + diff --git a/tools/sonarcloud-analysis b/tools/sonarcloud-analysis new file mode 100755 index 00000000000..9ff3a8440e4 --- /dev/null +++ b/tools/sonarcloud-analysis @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +TARGET=sonar-scanner-cli-4.7.0.2747-linux.zip +TARGET_DIR=sonar-scanner-4.7.0.2747-linux +curl https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/${TARGET} --output ${TARGET} +unzip ${TARGET} +cp tools/sonar-scanner.properties ${TARGET_DIR}/conf +chmod +x ${TARGET_DIR}/bin/sonar-scanner +./${TARGET_DIR}/bin/sonar-scanner From d86ccd594de12b01174667fe66c091ad897ff206 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 31 Oct 2022 07:30:21 +0100 Subject: [PATCH 029/426] feat(aptos): fix aptos global variable initialization for wasm (#2689) --- src/Aptos/Address.h | 16 ++++++-- src/Aptos/MoveTypes.h | 12 +++--- tests/chains/Aptos/AddressTests.cpp | 8 ++-- tests/chains/Aptos/MoveTypesTests.cpp | 12 +++--- .../chains/Aptos/TransactionPayloadTests.cpp | 2 +- wasm/tests/Blockchain/Aptos.test.ts | 40 +++++++++++++++++++ 6 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 wasm/tests/Blockchain/Aptos.test.ts diff --git a/src/Aptos/Address.h b/src/Aptos/Address.h index 45fe27aa011..328c387edf4 100644 --- a/src/Aptos/Address.h +++ b/src/Aptos/Address.h @@ -27,6 +27,18 @@ class Address { /// Determines whether a string makes a valid address. static bool isValid(const std::string& string); + static Address zero() { + return Address("0x0"); + } + + static Address one() { + return Address("0x1"); + } + + static Address three() { + return Address("0x3"); + } + /// Initializes an Aptos address with a string representation. explicit Address(const std::string& string); @@ -50,10 +62,6 @@ constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcep return lhs.bytes == rhs.bytes; } -inline const Address gAddressZero = Address("0x0"); -inline const Address gAddressOne = Address("0x1"); -inline const Address gAddressThree = Address("0x3"); - BCS::Serializer& operator<<(BCS::Serializer& stream, Address) noexcept; } // namespace TW::Aptos diff --git a/src/Aptos/MoveTypes.h b/src/Aptos/MoveTypes.h index c4360afcd83..f9662825921 100644 --- a/src/Aptos/MoveTypes.h +++ b/src/Aptos/MoveTypes.h @@ -33,10 +33,10 @@ class ModuleId { Identifier mName; }; -inline ModuleId gAptosAccountModule{gAddressOne, "aptos_account"}; -inline ModuleId gAptosCoinModule{gAddressOne, "coin"}; -inline ModuleId gAptosManagedCoinsModule{gAddressOne, "managed_coin"}; -inline ModuleId gAptosTokenTransfersModule{gAddressThree, "token_transfers"}; +inline ModuleId gAptosAccountModule{Address::one(), "aptos_account"}; +inline ModuleId gAptosCoinModule{Address::one(), "coin"}; +inline ModuleId gAptosManagedCoinsModule{Address::one(), "managed_coin"}; +inline ModuleId gAptosTokenTransfersModule{Address::three(), "token_transfers"}; BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept; @@ -101,7 +101,7 @@ BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept; BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept; BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& t) noexcept; BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept; -static const TypeTag gTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(gAddressOne, "aptos_coin", "AptosCoin", {})})}; -static const TypeTag gOfferNftTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(gAddressThree, "token_transfers", "offer_script", {})})}; +static const TypeTag gTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address::one(), "aptos_coin", "AptosCoin", {})})}; +static const TypeTag gOfferNftTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address::three(), "token_transfers", "offer_script", {})})}; } // namespace TW::Aptos diff --git a/tests/chains/Aptos/AddressTests.cpp b/tests/chains/Aptos/AddressTests.cpp index 92f156d077e..1267ea72cdd 100644 --- a/tests/chains/Aptos/AddressTests.cpp +++ b/tests/chains/Aptos/AddressTests.cpp @@ -16,8 +16,8 @@ namespace TW::Aptos::tests { TEST(AptosAddress, Valid) { ASSERT_TRUE(Address::isValid("0x1")); - ASSERT_TRUE(Address::isValid(gAddressOne.string())); - ASSERT_TRUE(Address::isValid(gAddressZero.string())); + ASSERT_TRUE(Address::isValid(Address::one().string())); + ASSERT_TRUE(Address::isValid(Address::zero().string())); ASSERT_TRUE(Address::isValid("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); ASSERT_TRUE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); ASSERT_TRUE(Address::isValid("19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c")); @@ -47,8 +47,8 @@ TEST(AptosAddress, FromString) { } TEST(AptosAddress, ShortString) { - ASSERT_EQ(gAddressOne.string(), "0x0000000000000000000000000000000000000000000000000000000000000001"); - ASSERT_EQ(gAddressOne.shortString(), "1"); + ASSERT_EQ(Address::one().string(), "0x0000000000000000000000000000000000000000000000000000000000000001"); + ASSERT_EQ(Address::one().shortString(), "1"); } } // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/MoveTypesTests.cpp b/tests/chains/Aptos/MoveTypesTests.cpp index c2cffeafa9a..3c7fded5d17 100644 --- a/tests/chains/Aptos/MoveTypesTests.cpp +++ b/tests/chains/Aptos/MoveTypesTests.cpp @@ -11,8 +11,8 @@ namespace TW::Aptos::tests { TEST(AptosMoveTypes, ModuleId) { - ModuleId module(gAddressOne, "coin"); - ASSERT_EQ(module.address(), gAddressOne); + ModuleId module(Address::one(), "coin"); + ASSERT_EQ(module.address(), Address::one()); ASSERT_EQ(module.name(), "coin"); ASSERT_EQ(hex(module.accessVector()), "00000000000000000000000000000000000000000000000000000000000000000104636f696e"); ASSERT_EQ(module.string(), "0x0000000000000000000000000000000000000000000000000000000000000001::coin"); @@ -22,9 +22,9 @@ TEST(AptosMoveTypes, ModuleId) { TEST(AptosMoveTypes, StructTag) { auto functorTest = [](T value, const std::string expectedHex) { TypeTag t{.tags = value}; - StructTag st(gAddressOne, "abc", "abc", std::vector{{t}}); + StructTag st(Address::one(), "abc", "abc", std::vector{{t}}); ASSERT_EQ(st.moduleID().name(), "abc"); - ASSERT_EQ(st.moduleID().address(), gAddressOne); + ASSERT_EQ(st.moduleID().address(), Address::one()); ASSERT_EQ(hex(st.serialize()), expectedHex); }; functorTest(Bool{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630100"); @@ -34,7 +34,7 @@ TEST(AptosMoveTypes, StructTag) { functorTest(TAddress{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630104"); functorTest(TSigner{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630105"); functorTest(Vector{.tags = std::vector{{TypeTag{.tags = U8{}}}}}, "0100000000000000000000000000000000000000000000000000000000000000010361626303616263010601"); - StructTag stInner(gAddressOne, "foo", "bar", std::vector{{U8{}}}); + StructTag stInner(Address::one(), "foo", "bar", std::vector{{U8{}}}); functorTest(TStructTag{stInner}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630107000000000000000000000000000000000000000000000000000000000000000103666f6f036261720101"); } @@ -50,7 +50,7 @@ TEST(AptosMoveTypes, TypeTagDisplay) { functorTest(TypeTag{.tags = TSigner{}}, "signer"); TypeTag t{.tags = TypeTag::TypeTagVariant(Vector{.tags = {{U8{}}}})}; functorTest(t, "vector"); - StructTag st(gAddressOne, "foo", "bar", std::vector{{U8{}}}); + StructTag st(Address::one(), "foo", "bar", std::vector{{U8{}}}); TypeTag anotherT{.tags = TypeTag::TypeTagVariant(st)}; functorTest(anotherT, "0x1::foo::bar"); functorTest(gTransferTag, "0x1::aptos_coin::AptosCoin"); diff --git a/tests/chains/Aptos/TransactionPayloadTests.cpp b/tests/chains/Aptos/TransactionPayloadTests.cpp index 29fd5e3712a..ed393963a06 100644 --- a/tests/chains/Aptos/TransactionPayloadTests.cpp +++ b/tests/chains/Aptos/TransactionPayloadTests.cpp @@ -11,7 +11,7 @@ namespace TW::Aptos::tests { TEST(AptosTransactionPayload, PayLoadBasis) { - ModuleId module(gAddressOne, "coin"); + ModuleId module(Address::one(), "coin"); std::uint64_t amount{1000}; Address to("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); BCS::Serializer serializer; diff --git a/wasm/tests/Blockchain/Aptos.test.ts b/wasm/tests/Blockchain/Aptos.test.ts new file mode 100644 index 00000000000..78486eab2bd --- /dev/null +++ b/wasm/tests/Blockchain/Aptos.test.ts @@ -0,0 +1,40 @@ +// Copyright © 2017-2022 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. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; +import { TW } from "../../dist"; +import Long = require("long"); + +describe("Aptos", () => { + it("test sign aptos", () => { + const { PrivateKey, HexCoding, AnySigner, AnyAddress, CoinType } = globalThis.core; + const txDataInput = TW.Aptos.Proto.SigningInput.create({ + chainId: 33, + sender: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + transfer: TW.Aptos.Proto.TransferMessage.create({ + amount: new Long(1000), + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + }), + sequenceNumber: new Long(99), + expirationTimestampSecs: new Long(3664390082), + gasUnitPrice: new Long(100), + maxGasAmount: new Long(3296766), + privateKey: PrivateKey.createWithData( + HexCoding.decode( + "0x5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", + ), + ).data(), + }); + const input = TW.Aptos.Proto.SigningInput.encode(txDataInput).finish(); + const outputData = AnySigner.sign(input, CoinType.aptos); + const output = TW.Aptos.Proto.SigningOutput.decode(outputData); + assert.equal(HexCoding.encode(output.encoded), "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01") + assert.equal(HexCoding.encode(output.authenticator!.signature), "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01") + assert.equal(HexCoding.encode(output.rawTxn), "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021") + }); +}); From 83465cf7a8b669cbccc1f2af63819c3f606bd2d1 Mon Sep 17 00:00:00 2001 From: hewigovens <360470+hewigovens@users.noreply.github.com> Date: Tue, 1 Nov 2022 08:06:17 +0900 Subject: [PATCH 030/426] [Swift] Fix some warnings (#2691) * Fix some warnings in test * set basedOnDependencyAnalysis false --- swift/Tests/TransactionCompilerTests.swift | 8 ++++---- swift/project.yml | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/swift/Tests/TransactionCompilerTests.swift b/swift/Tests/TransactionCompilerTests.swift index b5e147a65f5..05ff08f7457 100644 --- a/swift/Tests/TransactionCompilerTests.swift +++ b/swift/Tests/TransactionCompilerTests.swift @@ -143,8 +143,8 @@ class TransactionCompilerTests: XCTestCase { XCTAssertEqual(preSigningOutput.hashPublicKeys[2].publicKeyHash.hexString, inPubKeyHash0.hexString) // Simulate signatures, normally they are obtained from external source, e.g. a signature server. - var signatureVec = DataVector() - var pubkeyVec = DataVector() + let signatureVec = DataVector() + let pubkeyVec = DataVector() for h in preSigningOutput.hashPublicKeys { let preImageHash = h.dataHash let pubkeyHash = h.publicKeyHash @@ -153,14 +153,14 @@ class TransactionCompilerTests: XCTestCase { XCTAssertTrue(signatureInfos.contains { $0.key == key }) let sigInfo: SignatureInfo = signatureInfos[key]! let publicKeyData = sigInfo.publicKey - let publicKey = PublicKey(data: publicKeyData, type: PublicKeyType.secp256k1) + let publicKey = PublicKey(data: publicKeyData, type: PublicKeyType.secp256k1)! let signature = sigInfo.signature signatureVec.add(data: signature) pubkeyVec.add(data: publicKeyData) // Verify signature (pubkey & hash & signature) - publicKey?.verifyAsDER(signature: signature, message: preImageHash) + XCTAssertTrue(publicKey.verifyAsDER(signature: signature, message: preImageHash)) } /// Step 3: Compile transaction info diff --git a/swift/project.yml b/swift/project.yml index 3d17eb60784..274533d6a6b 100644 --- a/swift/project.yml +++ b/swift/project.yml @@ -60,6 +60,7 @@ targets: fi name: SwiftLint shell: /bin/bash + basedOnDependencyAnalysis: false WalletCoreTests: type: bundle.unit-test From 93465c4f33338f36b3aab93129796269cd3582d2 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Tue, 1 Nov 2022 00:06:38 +0100 Subject: [PATCH 031/426] Update in SmartBCH swift test (#2692) --- .../smartbitcoincash/TestSmartBitcoinCashAddress.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt index 852245d5a05..95acfd23453 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt @@ -20,12 +20,12 @@ class TestSmartBitcoinCashAddress { @Test fun testAddress() { - val key = PrivateKey("ab4accc9310d90a61fc354d8f353bca4a2b3c0590685d3eb82d0216af3badddc".toHexByteArray()) + val key = PrivateKey("155cbd57319f3d938977b4c18000473eb3c432c4e31b667b63e88559c497d82d".toHexByteArray()) val pubkey = key.getPublicKeySecp256k1(false) val address = AnyAddress(pubkey, CoinType.SMARTBITCOINCASH) - val expected = AnyAddress("0xA3Dcd899C0f3832DFDFed9479a9d828c6A4EB2A7", CoinType.SMARTBITCOINCASH) + val expected = AnyAddress("0x8bFC9477684987dcAf0970b9bce5E3D9267C99C0", CoinType.SMARTBITCOINCASH) - assertEquals(pubkey.data().toHex(), "0x0448a9ffac8022f1c7eb5253746e24d11d9b6b2737c0aecd48335feabb95a179916b1f3a97bed6740a85a2d11c663d38566acfb08af48a47ce0c835c65c9b23d0d") + assertEquals(pubkey.data().toHex(), "0x046439f94100c802691c53ef18523be2c24d301f0e2bd3b425e832378a5405eff4331d5e57303785969073321fc76a8504a3854bdb21e6ab7b268a1737882a29c0") assertEquals(address.description(), expected.description()) } } From 469b324177da1cc31e6ec07a4b155c3cbee39127 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 3 Nov 2022 13:08:27 +0100 Subject: [PATCH 032/426] feat(wasm): upgrade mocha version to 10.1.0 and 10.0.0 (#2698) --- wasm/package-lock.json | 189 +++++++++++++++-------------------------- wasm/package.json | 4 +- 2 files changed, 72 insertions(+), 121 deletions(-) diff --git a/wasm/package-lock.json b/wasm/package-lock.json index 8697fe149e1..7647bbb08c3 100644 --- a/wasm/package-lock.json +++ b/wasm/package-lock.json @@ -13,12 +13,12 @@ }, "devDependencies": { "@types/chai": "^4.3.0", - "@types/mocha": "^9.1.0", + "@types/mocha": "^10.0.0", "@types/node": "^18.7.18", "@types/webextension-polyfill": "^0.9.0", "buffer": "^6.0.3", "chai": "^4.3.6", - "mocha": "^9.2.2", + "mocha": "^10.1.0", "ts-node": "^10.7.0", "typescript": "^4.6.3" } @@ -134,9 +134,9 @@ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, "node_modules/@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", "dev": true }, "node_modules/@types/node": { @@ -150,12 +150,6 @@ "integrity": "sha512-HG1y1o2hK8ag6Y7dfkrAbfKmMIP+B0E6SwAzUfmQ1dDxEIdLTtMyrStY26suHBPrAL7Xw/chlDW02ugc3uXWtQ==", "dev": true }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "node_modules/acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", @@ -467,9 +461,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -668,15 +662,6 @@ "node": "*" } }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -803,12 +788,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -873,54 +852,60 @@ "dev": true }, "node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", + "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", "dev": true, "dependencies": { - "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", - "debug": "4.3.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", - "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "4.2.1", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.1", + "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14.0.0" }, "funding": { "type": "opencollective", @@ -943,9 +928,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -1261,25 +1246,10 @@ "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", "dev": true }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "node_modules/wrap-ansi": { @@ -1484,9 +1454,9 @@ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, "@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", "dev": true }, "@types/node": { @@ -1500,12 +1470,6 @@ "integrity": "sha512-HG1y1o2hK8ag6Y7dfkrAbfKmMIP+B0E6SwAzUfmQ1dDxEIdLTtMyrStY26suHBPrAL7Xw/chlDW02ugc3uXWtQ==", "dev": true }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", @@ -1723,9 +1687,9 @@ "dev": true }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -1862,12 +1826,6 @@ "is-glob": "^4.0.1" } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1950,12 +1908,6 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2005,41 +1957,49 @@ "dev": true }, "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } } }, "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", + "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", "dev": true, "requires": { - "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", - "debug": "4.3.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", - "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "4.2.1", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.1", + "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -2060,9 +2020,9 @@ "dev": true }, "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, "normalize-path": { @@ -2264,19 +2224,10 @@ "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", "dev": true }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "wrap-ansi": { diff --git a/wasm/package.json b/wasm/package.json index 8ae7e3f0b3b..eafc3845ed4 100644 --- a/wasm/package.json +++ b/wasm/package.json @@ -34,12 +34,12 @@ }, "devDependencies": { "@types/chai": "^4.3.0", - "@types/mocha": "^9.1.0", + "@types/mocha": "^10.0.0", "@types/node": "^18.7.18", "@types/webextension-polyfill": "^0.9.0", "buffer": "^6.0.3", "chai": "^4.3.6", - "mocha": "^9.2.2", + "mocha": "^10.1.0", "ts-node": "^10.7.0", "typescript": "^4.6.3" } From 748d5a745b61e48665ae4ca122f26269363080db Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 3 Nov 2022 17:29:35 +0100 Subject: [PATCH 033/426] [Solana]: add unsigned_tx support (#2699) * feat(solana): add unsigned_tx base58 to SigningOutput --- src/Solana/Signer.cpp | 3 +++ src/proto/Solana.proto | 3 +++ tests/chains/Solana/TWAnySignerTests.cpp | 1 + 3 files changed, 7 insertions(+) diff --git a/src/Solana/Signer.cpp b/src/Solana/Signer.cpp index f5839bc0e8f..e14351939fd 100644 --- a/src/Solana/Signer.cpp +++ b/src/Solana/Signer.cpp @@ -173,6 +173,9 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto encoded = transaction.serialize(); protoOutput.set_encoded(encoded); + auto unsignedTx = Base58::bitcoin.encode(transaction.messageData()); + protoOutput.set_unsigned_tx(unsignedTx.data(), unsignedTx.size()); + return protoOutput; } diff --git a/src/proto/Solana.proto b/src/proto/Solana.proto index 0f7280b5da3..07fb6a00f58 100644 --- a/src/proto/Solana.proto +++ b/src/proto/Solana.proto @@ -156,4 +156,7 @@ message SigningInput { message SigningOutput { // The encoded transaction string encoded = 1; + + // The unsigned transaction + string unsigned_tx = 2; } diff --git a/tests/chains/Solana/TWAnySignerTests.cpp b/tests/chains/Solana/TWAnySignerTests.cpp index 8246f8f48ce..5fd474bbf0e 100644 --- a/tests/chains/Solana/TWAnySignerTests.cpp +++ b/tests/chains/Solana/TWAnySignerTests.cpp @@ -38,6 +38,7 @@ TEST(TWAnySignerSolana, SignTransfer) { ANY_SIGN(input, TWCoinTypeSolana); ASSERT_EQ(output.encoded(), expectedString1); + ASSERT_EQ(output.unsigned_tx(), "87PYsiS4MUU1UqXrsDoCBmD5FcKsXhwEBD8hc4zbq78yePu7bLENmbnmjmVbsj4VvaxnZhy4bERndPFzjSRH5WpwKwMLSCKvn9eSDmPESNcdkqne2UdMfWiFoq8ZeQBnF9h98dP8GM9kfzWPjvLmhjwuwA1E2k5WCtfii7LKQ34v6AtmFQGZqgdKiNqygP7ZKusHWGT8ZkTZ"); } TEST(TWAnySignerSolana, SignTransferToSelf) { From 6d452504d9ca4403a3923cd7879ccc4a871b0c0d Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Fri, 4 Nov 2022 14:12:48 +0100 Subject: [PATCH 034/426] Expose derivation option in AnyAddress, HDWallet (#2683) * Expose derivation option on TWAnyAddressCreateWithPublicKey * Expose derivation option on TWHDWalletGetKey * HDWalletGetAddressDerivation: expose derivation in wallet.getAddress * Devconsole: update wc lib to latest * New sample script for devConsole * Use \see in comments * Swift test for AnyAddress * Swift test for new HDWallet methods * Kotlin test for new HDWallet methods * PublicKeyType fix * Review: assertStringsEqual(), const rename. --- .../core/app/utils/TestAnyAddress.kt | 92 +++++++++++++++++++ .../core/app/utils/TestHDWallet.kt | 54 +++++++++++ include/TrustWalletCore/TWAnyAddress.h | 9 ++ include/TrustWalletCore/TWHDWallet.h | 33 ++++++- .../devconsole.ts/package-lock.json | 14 +-- samples/typescript/devconsole.ts/package.json | 2 +- .../bitcoin.addresses.sampleinput.txt | 19 ++++ src/AnyAddress.cpp | 8 +- src/AnyAddress.h | 4 +- src/interface/TWAnyAddress.cpp | 5 + src/interface/TWHDWallet.cpp | 18 +++- swift/Tests/AnyAddressTests.swift | 69 ++++++++++++++ swift/Tests/HDWalletTests.swift | 44 ++++++++- tests/common/AnyAddressTests.cpp | 13 +++ tests/interface/TWAnyAddressTests.cpp | 38 +++++++- tests/interface/TWHDWalletTests.cpp | 32 ++++++- 16 files changed, 427 insertions(+), 27 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt create mode 100644 samples/typescript/devconsole.ts/samplescripts/bitcoin.addresses.sampleinput.txt create mode 100644 swift/Tests/AnyAddressTests.swift diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt new file mode 100644 index 00000000000..64b3bb82987 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt @@ -0,0 +1,92 @@ +// Copyright © 2017-2022 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.utils + +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import wallet.core.jni.* +import java.security.InvalidParameterException +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Test + +class TestAnyAddress { + init { + System.loadLibrary("TrustWalletCore"); + } + + val any_address_test_address = "bc1qcj2vfjec3c3luf9fx9vddnglhh9gawmncmgxhz" + val any_address_test_pubkey = "02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc" + + @Test + fun testCreateWithString() { + val coin = CoinType.BITCOIN + val address = AnyAddress(any_address_test_address, coin) + assertEquals(address.coin(), coin) + assertEquals(address.description(), any_address_test_address) + } + + @Test + fun testCreateWithStringBech32() { + val coin = CoinType.BITCOIN + val address1 = AnyAddress(any_address_test_address, coin, "bc") + assertEquals(address1.description(), any_address_test_address) + + val address2 = AnyAddress("tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3", coin, "tb") + assertEquals(address2.description(), "tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3") + } + + @Test + fun testCreateWithPublicKey() { + val coin = CoinType.BITCOIN + val pubkey = PublicKey(any_address_test_pubkey.toHexByteArray(), PublicKeyType.SECP256K1) + val address = AnyAddress(pubkey, coin) + assertEquals(address.description(), any_address_test_address) + } + + @Test + fun testCreateWithPublicKeyDerivation() { + val coin = CoinType.BITCOIN + val pubkey = PublicKey(any_address_test_pubkey.toHexByteArray(), PublicKeyType.SECP256K1) + val address1 = AnyAddress(pubkey, coin, Derivation.BITCOINSEGWIT) + assertEquals(address1.description(), any_address_test_address) + + val address2 = AnyAddress(pubkey, coin, Derivation.BITCOINLEGACY) + assertEquals(address2.description(), "1JvRfEQFv5q5qy9uTSAezH7kVQf4hqnHXx") + } + + @Test + fun testCreateBech32WithPublicKey() { + val coin = CoinType.BITCOIN + val pubkey = PublicKey(any_address_test_pubkey.toHexByteArray(), PublicKeyType.SECP256K1) + val address1 = AnyAddress(pubkey, coin, "bc") + assertEquals(address1.description(), any_address_test_address) + + val address2 = AnyAddress(pubkey, coin, "tb") + assertEquals(address2.description(), "tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3") + } + + @Test + fun testIsValid() { + val coin = CoinType.BITCOIN + assertTrue(AnyAddress.isValid(any_address_test_address, coin)); + assertFalse(AnyAddress.isValid(any_address_test_address, CoinType.ETHEREUM)); + assertFalse(AnyAddress.isValid("__INVALID_ADDRESS__", CoinType.ETHEREUM)); + } + + @Test + fun testIsValidBech32() { + val coin = CoinType.BITCOIN + assertTrue(AnyAddress.isValidBech32(any_address_test_address, coin, "bc")); + assertFalse(AnyAddress.isValidBech32(any_address_test_address, coin, "tb")); + assertTrue(AnyAddress.isValidBech32("tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3", coin, "tb")); + assertFalse(AnyAddress.isValidBech32("tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3", coin, "bc")); + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt index 0d85be821d8..ed1c171aa6b 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt @@ -1,8 +1,16 @@ +// Copyright © 2017-2022 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.utils import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHex import wallet.core.jni.CoinType import wallet.core.jni.Curve +import wallet.core.jni.Derivation import wallet.core.jni.HDVersion import wallet.core.jni.HDWallet import wallet.core.jni.Mnemonic @@ -65,6 +73,52 @@ class TestHDWallet { assertEquals(Numeric.toHexString(hd.entropy()), "0xba5821e8c356c05ba5f025d9532fe0f21f65d594") } + @Test + fun testGetKeyForCoin() { + val coin = CoinType.BITCOIN + val wallet = HDWallet(words, password) + val key = wallet.getKeyForCoin(coin) + + val address = coin.deriveAddress(key) + assertEquals(address, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85") + } + + @Test + fun testGetKeyDerivation() { + val coin = CoinType.BITCOIN + val wallet = HDWallet(words, password) + + val key1 = wallet.getKeyDerivation(coin, Derivation.BITCOINSEGWIT) + assertEquals(key1.data().toHex(), "0x1901b5994f075af71397f65bd68a9fff8d3025d65f5a2c731cf90f5e259d6aac") + + val key2 = wallet.getKeyDerivation(coin, Derivation.BITCOINLEGACY) + assertEquals(key2.data().toHex(), "0x28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4") + + val key3 = wallet.getKeyDerivation(coin, Derivation.BITCOINTESTNET) + assertEquals(key3.data().toHex(), "0xca5845e1b43e3adf577b7f110b60596479425695005a594c88f9901c3afe864f") + } + + @Test + fun testGetAddressForCoin() { + val coin = CoinType.BITCOIN + val wallet = HDWallet(words, password) + + val address = wallet.getAddressForCoin(coin) + assertEquals(address, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85") + } + + @Test + fun testGetAddressDerivation() { + val coin = CoinType.BITCOIN + val wallet = HDWallet(words, password) + + val address1 = wallet.getAddressDerivation(coin, Derivation.BITCOINSEGWIT) + assertEquals(address1, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85") + + val address2 = wallet.getAddressDerivation(coin, Derivation.BITCOINLEGACY) + assertEquals(address2, "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1") + } + @Test fun testDerive() { val wallet = HDWallet(words, password) diff --git a/include/TrustWalletCore/TWAnyAddress.h b/include/TrustWalletCore/TWAnyAddress.h index 86dcdc51bd9..652d4994529 100644 --- a/include/TrustWalletCore/TWAnyAddress.h +++ b/include/TrustWalletCore/TWAnyAddress.h @@ -88,6 +88,15 @@ struct TWAnyAddress* _Nullable TWAnyAddressCreateSS58(TWString* _Nonnull string, TW_EXPORT_STATIC_METHOD struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKey(struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin); +/// Creates an address from a public key and derivation option. +/// +/// \param publicKey derivates the address from the public key. +/// \param coin coin type of the address. +/// \param derivation the custom derivation to use. +/// \return TWAnyAddress pointer or nullptr if public key is invalid. +TW_EXPORT_STATIC_METHOD +struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKeyDerivation(struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, enum TWDerivation derivation); + /// Creates an bech32 address from a public key and a given hrp. /// /// \param publicKey derivates the address from the public key. diff --git a/include/TrustWalletCore/TWHDWallet.h b/include/TrustWalletCore/TWHDWallet.h index 55ab3e13b4a..a6261f57a7b 100644 --- a/include/TrustWalletCore/TWHDWallet.h +++ b/include/TrustWalletCore/TWHDWallet.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -100,8 +100,10 @@ TWData* _Nonnull TWHDWalletEntropy(struct TWHDWallet* _Nonnull wallet); TW_EXPORT_METHOD struct TWPrivateKey* _Nonnull TWHDWalletGetMasterKey(struct TWHDWallet* _Nonnull wallet, enum TWCurve curve); -/// Generates the default private key for the specified coin. +/// Generates the default private key for the specified coin, using default derivation. /// +/// \see TWHDWalletGetKey +/// \see TWHDWalletGetKeyDerivation /// \param wallet non-null TWHDWallet /// \param coin a coin type /// \note Returned object needs to be deleted with \TWPrivateKeyDelete @@ -109,16 +111,29 @@ struct TWPrivateKey* _Nonnull TWHDWalletGetMasterKey(struct TWHDWallet* _Nonnull TW_EXPORT_METHOD struct TWPrivateKey* _Nonnull TWHDWalletGetKeyForCoin(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin); -/// Generates the default address for the specified coin (without exposing intermediary private key). +/// Generates the default address for the specified coin (without exposing intermediary private key), default derivation. /// +/// \see TWHDWalletGetAddressDerivation /// \param wallet non-null TWHDWallet /// \param coin a coin type /// \return return the default address for the specified coin as a non-null TWString TW_EXPORT_METHOD TWString* _Nonnull TWHDWalletGetAddressForCoin(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin); +/// Generates the default address for the specified coin and derivation (without exposing intermediary private key). +/// +/// \see TWHDWalletGetAddressForCoin +/// \param wallet non-null TWHDWallet +/// \param coin a coin type +/// \param derivation a (custom) derivation to use +/// \return return the default address for the specified coin as a non-null TWString +TW_EXPORT_METHOD +TWString* _Nonnull TWHDWalletGetAddressDerivation(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation); + /// Generates the private key for the specified derivation path. /// +/// \see TWHDWalletGetKeyForCoin +/// \see TWHDWalletGetKeyDerivation /// \param wallet non-null TWHDWallet /// \param coin a coin type /// \param derivationPath a non-null derivation path @@ -127,6 +142,18 @@ TWString* _Nonnull TWHDWalletGetAddressForCoin(struct TWHDWallet* _Nonnull walle TW_EXPORT_METHOD struct TWPrivateKey* _Nonnull TWHDWalletGetKey(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, TWString* _Nonnull derivationPath); +/// Generates the private key for the specified derivation. +/// +/// \see TWHDWalletGetKey +/// \see TWHDWalletGetKeyForCoin +/// \param wallet non-null TWHDWallet +/// \param coin a coin type +/// \param derivation a (custom) derivation to use +/// \note Returned object needs to be deleted with \TWPrivateKeyDelete +/// \return The private key for the specified derivation path/coin +TW_EXPORT_METHOD +struct TWPrivateKey* _Nonnull TWHDWalletGetKeyDerivation(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation); + /// Generates the private key for the specified derivation path and curve. /// /// \param wallet non-null TWHDWallet diff --git a/samples/typescript/devconsole.ts/package-lock.json b/samples/typescript/devconsole.ts/package-lock.json index 50046ba9ab4..ce8a4ecf0ee 100644 --- a/samples/typescript/devconsole.ts/package-lock.json +++ b/samples/typescript/devconsole.ts/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@trustwallet/wallet-core": "3.0.4", + "@trustwallet/wallet-core": "3.0.8", "chalk": "^4.1.2", "clear": "^0.1.0", "figlet": "^1.5.2", @@ -116,9 +116,9 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@trustwallet/wallet-core": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@trustwallet/wallet-core/-/wallet-core-3.0.4.tgz", - "integrity": "sha512-FrIVEwRmUYFuwU9IoXg0J8fngeW7nlJZZAsrnOWba2g/yGWZl4FQ4z87MEQ5ROOGW9jJzsdWG4PRZffvYzhqsg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@trustwallet/wallet-core/-/wallet-core-3.0.8.tgz", + "integrity": "sha512-u0ST2xZL6oWCAMmZRKQVxBLhNtOu/tYByVl9OEfd8SbAGHVjW/6V+oVmHAqBL/z3fwfZfYg2LY08AW831ZNwoQ==", "dependencies": { "protobufjs": ">=6.11.3" } @@ -964,9 +964,9 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "@trustwallet/wallet-core": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@trustwallet/wallet-core/-/wallet-core-3.0.4.tgz", - "integrity": "sha512-FrIVEwRmUYFuwU9IoXg0J8fngeW7nlJZZAsrnOWba2g/yGWZl4FQ4z87MEQ5ROOGW9jJzsdWG4PRZffvYzhqsg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@trustwallet/wallet-core/-/wallet-core-3.0.8.tgz", + "integrity": "sha512-u0ST2xZL6oWCAMmZRKQVxBLhNtOu/tYByVl9OEfd8SbAGHVjW/6V+oVmHAqBL/z3fwfZfYg2LY08AW831ZNwoQ==", "requires": { "protobufjs": ">=6.11.3" } diff --git a/samples/typescript/devconsole.ts/package.json b/samples/typescript/devconsole.ts/package.json index 7ca581061d5..64e52dc3f10 100644 --- a/samples/typescript/devconsole.ts/package.json +++ b/samples/typescript/devconsole.ts/package.json @@ -14,7 +14,7 @@ "author": "Trust Wallet", "license": "MIT", "dependencies": { - "@trustwallet/wallet-core": "3.0.4", + "@trustwallet/wallet-core": "3.0.8", "chalk": "^4.1.2", "clear": "^0.1.0", "figlet": "^1.5.2", diff --git a/samples/typescript/devconsole.ts/samplescripts/bitcoin.addresses.sampleinput.txt b/samples/typescript/devconsole.ts/samplescripts/bitcoin.addresses.sampleinput.txt new file mode 100644 index 00000000000..9c7201110a7 --- /dev/null +++ b/samples/typescript/devconsole.ts/samplescripts/bitcoin.addresses.sampleinput.txt @@ -0,0 +1,19 @@ +// Derive Bitcoin addresses of different flavor + +coin = CoinType.bitcoin + +privKeyData = '0xafeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5'; +privKey = PrivateKey.createWithData(HexCoding.decode(privKeyData)); +pubKey = privKey.getPublicKeySecp256k1(true); +address11 = SegwitAddress.createWithPublicKey(HRP.bitcoin, pubKey).description(); + +// This is not yet possible yet through AnyAddress +// TWAnyAddressCreateWithPublicKeyDerivation +address21 = AnyAddress.createWithPublicKey(pubKey, coin); +address21.description() + +// This is not yet possible yet through HDWallet +// TWHDWalletGetKeyDerivation +// TWHDWalletGetAddressDerivation +wallet = HDWallet.createWithMnemonic('ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal', ''); +address = wallet.getAddressForCoin(coin); diff --git a/src/AnyAddress.cpp b/src/AnyAddress.cpp index be44ccb2e95..bf0fb994b98 100644 --- a/src/AnyAddress.cpp +++ b/src/AnyAddress.cpp @@ -25,15 +25,15 @@ AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinTyp return new AnyAddress{.address = std::move(normalized), .coin = coin}; } -AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp) { +AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp, TWDerivation derivation) { - auto derivedAddress = TW::deriveAddress(coin, publicKey, TWDerivationDefault, hrp); + auto derivedAddress = TW::deriveAddress(coin, publicKey, derivation, hrp); return new AnyAddress{.address = std::move(derivedAddress), .coin = coin}; } -AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const PrefixVariant& addressPrefix) { +AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const PrefixVariant& addressPrefix, TWDerivation derivation) { - auto derivedAddress = TW::deriveAddress(coin, publicKey, addressPrefix); + auto derivedAddress = TW::deriveAddress(coin, publicKey, addressPrefix, derivation); return new AnyAddress{.address = std::move(derivedAddress), .coin = coin}; } diff --git a/src/AnyAddress.h b/src/AnyAddress.h index a40be466a8e..e78f3d67a7c 100644 --- a/src/AnyAddress.h +++ b/src/AnyAddress.h @@ -24,8 +24,8 @@ class AnyAddress { enum TWCoinType coin; static AnyAddress* createAddress(const std::string& address, enum TWCoinType coin, const PrefixVariant& prefix = std::monostate()); - static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp = ""); - static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const PrefixVariant& prefix); + static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp = "", TWDerivation derivation = TWDerivationDefault); + static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const PrefixVariant& prefix, TWDerivation derivation = TWDerivationDefault); Data getData() const; }; diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index d67496008d4..4f4713679ed 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -68,6 +68,11 @@ struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKey( return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin)}; } +struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKeyDerivation( + struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, enum TWDerivation derivation) { + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, std::string(""), derivation)}; +} + struct TWAnyAddress* _Nonnull TWAnyAddressCreateBech32WithPublicKey( struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, TWString* _Nonnull hrp) { const auto& hrpStr = *reinterpret_cast(hrp); diff --git a/src/interface/TWHDWallet.cpp b/src/interface/TWHDWallet.cpp index 18447aa54bd..630b0613110 100644 --- a/src/interface/TWHDWallet.cpp +++ b/src/interface/TWHDWallet.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -67,14 +67,17 @@ struct TWPrivateKey *_Nonnull TWHDWalletGetMasterKey(struct TWHDWallet *_Nonnull } struct TWPrivateKey *_Nonnull TWHDWalletGetKeyForCoin(struct TWHDWallet *wallet, TWCoinType coin) { - auto derivationPath = TW::derivationPath(coin); - return new TWPrivateKey{ wallet->impl.getKey(coin, derivationPath) }; + return TWHDWalletGetKeyDerivation(wallet, coin, TWDerivationDefault); } TWString *_Nonnull TWHDWalletGetAddressForCoin(struct TWHDWallet *wallet, TWCoinType coin) { - auto derivationPath = TW::derivationPath(coin); + return TWHDWalletGetAddressDerivation(wallet, coin, TWDerivationDefault); +} + +TWString *_Nonnull TWHDWalletGetAddressDerivation(struct TWHDWallet *wallet, TWCoinType coin, enum TWDerivation derivation) { + auto derivationPath = TW::derivationPath(coin, derivation); PrivateKey privateKey = wallet->impl.getKey(coin, derivationPath); - std::string address = deriveAddress(coin, privateKey); + std::string address = deriveAddress(coin, privateKey, derivation); return TWStringCreateWithUTF8Bytes(address.c_str()); } @@ -84,6 +87,11 @@ struct TWPrivateKey *_Nonnull TWHDWalletGetKey(struct TWHDWallet *_Nonnull walle return new TWPrivateKey{ wallet->impl.getKey(coin, path) }; } +struct TWPrivateKey *_Nonnull TWHDWalletGetKeyDerivation(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation) { + auto derivationPath = TW::derivationPath(coin, derivation); + return new TWPrivateKey{ wallet->impl.getKey(coin, derivationPath) }; +} + struct TWPrivateKey *_Nonnull TWHDWalletGetDerivedKey(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, uint32_t account, uint32_t change, uint32_t address) { const auto derivationPath = DerivationPath(TW::purpose(coin), TW::slip44Id(coin), account, change, address); return new TWPrivateKey{ wallet->impl.getKey(coin, derivationPath) }; diff --git a/swift/Tests/AnyAddressTests.swift b/swift/Tests/AnyAddressTests.swift new file mode 100644 index 00000000000..dc4f16da527 --- /dev/null +++ b/swift/Tests/AnyAddressTests.swift @@ -0,0 +1,69 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class AnyAddressTests: XCTestCase { + let any_address_test_address = "bc1qcj2vfjec3c3luf9fx9vddnglhh9gawmncmgxhz" + let any_address_test_pubkey = "02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc" + + func testCreateWithString() { + let coin = CoinType.bitcoin + let address = AnyAddress(string: any_address_test_address, coin: coin)! + XCTAssertEqual(address.coin, coin) + XCTAssertEqual(address.description, any_address_test_address) + } + + func testCreateWithStringBech32() { + let coin = CoinType.bitcoin + let address1 = AnyAddress(string: any_address_test_address, coin: coin, hrp: "bc")! + XCTAssertEqual(address1.description, any_address_test_address) + + let address2 = AnyAddress(string: "tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3", coin: coin, hrp: "tb")! + XCTAssertEqual(address2.description, "tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3") + } + + func testCreateWithPublicKey() { + let coin = CoinType.bitcoin + let pubkey = PublicKey(data: Data(hexString: any_address_test_pubkey)!, type: .secp256k1)! + let address = AnyAddress(publicKey: pubkey, coin: coin) + XCTAssertEqual(address.description, any_address_test_address) + } + + func testCreateWithPublicKeyDerivation() { + let coin = CoinType.bitcoin + let pubkey = PublicKey(data: Data(hexString: any_address_test_pubkey)!, type: .secp256k1)! + let address1 = AnyAddress(publicKey: pubkey, coin: coin, derivation: .bitcoinSegwit) + XCTAssertEqual(address1.description, any_address_test_address) + + let address2 = AnyAddress(publicKey: pubkey, coin: coin, derivation: .bitcoinLegacy) + XCTAssertEqual(address2.description, "1JvRfEQFv5q5qy9uTSAezH7kVQf4hqnHXx") + } + + func testCreateBech32WithPublicKey() { + let coin = CoinType.bitcoin + let pubkey = PublicKey(data: Data(hexString: any_address_test_pubkey)!, type: .secp256k1)! + let address1 = AnyAddress(publicKey: pubkey, coin: coin, hrp: "bc") + XCTAssertEqual(address1.description, any_address_test_address) + + let address2 = AnyAddress(publicKey: pubkey, coin: coin, hrp: "tb") + XCTAssertEqual(address2.description, "tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3") + } + + func testIsValid() { + let coin = CoinType.bitcoin + XCTAssertTrue(AnyAddress.isValid(string: any_address_test_address, coin: coin)); + XCTAssertFalse(AnyAddress.isValid(string: any_address_test_address, coin: .ethereum)); + XCTAssertFalse(AnyAddress.isValid(string: "__INVALID_ADDRESS__", coin: .ethereum)); + } + + func testIsValidBech32() { + let coin = CoinType.bitcoin + XCTAssertTrue(AnyAddress.isValidBech32(string: any_address_test_address, coin: coin, hrp: "bc")); + XCTAssertFalse(AnyAddress.isValidBech32(string: any_address_test_address, coin: coin, hrp: "tb")); + } +} diff --git a/swift/Tests/HDWalletTests.swift b/swift/Tests/HDWalletTests.swift index e6e5b8b6b46..cc832c55a7e 100644 --- a/swift/Tests/HDWalletTests.swift +++ b/swift/Tests/HDWalletTests.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -49,6 +49,48 @@ class HDWalletTests: XCTestCase { XCTAssertEqual(masterKey.data.hexString, "e120fc1ef9d193a851926ebd937c3985dc2c4e642fb3d0832317884d5f18f3b3") } + func testGetKeyForCoin() { + let coin = CoinType.bitcoin + let wallet = HDWallet.test + let key = wallet.getKeyForCoin(coin: coin) + + let address = coin.deriveAddress(privateKey: key) + XCTAssertEqual(address, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85") + } + + func testGetKeyDerivation() { + let coin = CoinType.bitcoin + let wallet = HDWallet.test + + let key1 = wallet.getKeyDerivation(coin: coin, derivation: .bitcoinSegwit) + XCTAssertEqual(key1.data.hexString, "1901b5994f075af71397f65bd68a9fff8d3025d65f5a2c731cf90f5e259d6aac") + + let key2 = wallet.getKeyDerivation(coin: coin, derivation: .bitcoinLegacy) + XCTAssertEqual(key2.data.hexString, "28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4") + + let key3 = wallet.getKeyDerivation(coin: coin, derivation: .bitcoinTestnet) + XCTAssertEqual(key3.data.hexString, "ca5845e1b43e3adf577b7f110b60596479425695005a594c88f9901c3afe864f") + } + + func testGetAddressForCoin() { + let coin = CoinType.bitcoin + let wallet = HDWallet.test + + let address = wallet.getAddressForCoin(coin: coin) + XCTAssertEqual(address, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85") + } + + func testGetAddressDerivation() { + let coin = CoinType.bitcoin + let wallet = HDWallet.test + + let address1 = wallet.getAddressDerivation(coin: coin, derivation: .bitcoinSegwit) + XCTAssertEqual(address1, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85") + + let address2 = wallet.getAddressDerivation(coin: coin, derivation: .bitcoinLegacy) + XCTAssertEqual(address2, "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1") + } + func testDerive() { let wallet = HDWallet.test diff --git a/tests/common/AnyAddressTests.cpp b/tests/common/AnyAddressTests.cpp index bc7dc5af2b2..917bd0ca07d 100644 --- a/tests/common/AnyAddressTests.cpp +++ b/tests/common/AnyAddressTests.cpp @@ -26,6 +26,19 @@ TEST(AnyAddress, createFromPubKey) { EXPECT_EQ(ANY_ADDRESS_TEST_ADDRESS, addr->address); } +TEST(AnyAddress, createFromPubKeyDerivation) { + const Data key = parse_hex(ANY_ADDRESS_TEST_PUBKEY); + PublicKey publicKey(key, TWPublicKeyTypeSECP256k1); + { + std::unique_ptr addr(AnyAddress::createAddress(publicKey, TWCoinTypeBitcoin, std::string(""), TWDerivationDefault)); + EXPECT_EQ(addr->address, ANY_ADDRESS_TEST_ADDRESS); + } + { + std::unique_ptr addr(AnyAddress::createAddress(publicKey, TWCoinTypeBitcoin, std::string(""), TWDerivationBitcoinLegacy)); + EXPECT_EQ(addr->address, "1JvRfEQFv5q5qy9uTSAezH7kVQf4hqnHXx"); + } +} + TEST(AnyAddress, createFromWrongString) { std::unique_ptr addr(AnyAddress::createAddress("1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax", TWCoinTypeBitcoin)); EXPECT_EQ(nullptr, addr); diff --git a/tests/interface/TWAnyAddressTests.cpp b/tests/interface/TWAnyAddressTests.cpp index c0bef30ab93..f0384bd4c09 100644 --- a/tests/interface/TWAnyAddressTests.cpp +++ b/tests/interface/TWAnyAddressTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,12 +9,13 @@ #include "HexCoding.h" #include #include +#include #include using namespace TW; -TEST(AnyAddress, InvalidString) { +TEST(TWAnyAddress, InvalidString) { auto string = STRING("0x4E5B2e1dc63F6b91cb6Cd759936495434C7e972F"); auto btcAddress = TWAnyAddressCreateWithString(string.get(), TWCoinTypeBitcoin); auto ethAaddress = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeEthereum)); @@ -24,7 +25,7 @@ TEST(AnyAddress, InvalidString) { ASSERT_EQ(TWAnyAddressCoin(ethAaddress.get()), TWCoinTypeEthereum); } -TEST(AnyAddress, Data) { +TEST(TWAnyAddress, Data) { // ethereum { auto string = STRING("0x4E5B2e1dc63F6b91cb6Cd759936495434C7e972F"); @@ -169,3 +170,34 @@ TEST(AnyAddress, Data) { assertHexEqual(keyHash, "18f9d8d877393bbbe8d697a8a2e52879cc7e84f467656d1cce6bab5a8d2637ec"); } } + +TEST(TWAnyAddress, createFromPubKey) { + constexpr auto pubkey = "02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc"; + const auto pubkey_twstring = STRING(pubkey); + const auto pubkey_data = WRAPD(TWDataCreateWithHexString(pubkey_twstring.get())); + const auto pubkey_obj = WRAP(TWPublicKey, TWPublicKeyCreateWithData(pubkey_data.get(), TWPublicKeyTypeSECP256k1)); + + const auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(pubkey_obj.get(), TWCoinTypeBitcoin)); + + assertStringsEqual(WRAPS(TWAnyAddressDescription(addr.get())), "bc1qcj2vfjec3c3luf9fx9vddnglhh9gawmncmgxhz"); +} + +TEST(TWAnyAddress, createFromPubKeyDerivation) { + constexpr auto pubkey = "02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc"; + const auto pubkey_twstring = STRING(pubkey); + const auto pubkey_data = WRAPD(TWDataCreateWithHexString(pubkey_twstring.get())); + const auto pubkey_obj = WRAP(TWPublicKey, TWPublicKeyCreateWithData(pubkey_data.get(), TWPublicKeyTypeSECP256k1)); + + { + const auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKeyDerivation(pubkey_obj.get(), TWCoinTypeBitcoin, TWDerivationDefault)); + assertStringsEqual(WRAPS(TWAnyAddressDescription(addr.get())), "bc1qcj2vfjec3c3luf9fx9vddnglhh9gawmncmgxhz"); + } + { + const auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKeyDerivation(pubkey_obj.get(), TWCoinTypeBitcoin, TWDerivationBitcoinLegacy)); + assertStringsEqual(WRAPS(TWAnyAddressDescription(addr.get())), "1JvRfEQFv5q5qy9uTSAezH7kVQf4hqnHXx"); + } + { + const auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKeyDerivation(pubkey_obj.get(), TWCoinTypeBitcoin, TWDerivationBitcoinTestnet)); + assertStringsEqual(WRAPS(TWAnyAddressDescription(addr.get())), "tb1qcj2vfjec3c3luf9fx9vddnglhh9gawmnjan4v3"); + } +} diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index 63fc42fec3c..d34edd03468 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -133,12 +133,42 @@ TEST(HDWallet, DeriveBitcoinExtended) { assertStringsEqual(address, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85"); } +TEST(HDWallet, GetKeyDerivation) { + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); + { + auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyDerivation(wallet.get(), TWCoinTypeBitcoin, TWDerivationBitcoinSegwit)); + assertHexEqual(WRAPD(TWPrivateKeyData(key.get())), "1901b5994f075af71397f65bd68a9fff8d3025d65f5a2c731cf90f5e259d6aac"); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(key.get(), true)); + auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); + assertHexEqual(publicKeyData, "037ea5dff03f677502c4a1d73c5ac897200e56b155e876774c8fba0cc22f80b941"); + } + { + auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyDerivation(wallet.get(), TWCoinTypeBitcoin, TWDerivationBitcoinLegacy)); + assertHexEqual(WRAPD(TWPrivateKeyData(key.get())), "28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4"); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(key.get(), true)); + auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); + assertHexEqual(publicKeyData, "0240ebf906b948281289405317a5eb9a98045af8a8ab5311b2e3060cfb66c507a1"); + } +} + TEST(HDWallet, DeriveAddressBitcoin) { auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto address = WRAP(TWString, TWHDWalletGetAddressForCoin(wallet.get(), TWCoinTypeBitcoin)); assertStringsEqual(address, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85"); } +TEST(HDWallet, DeriveAddressBitcoinDerivation) { + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); + { + auto address = WRAP(TWString, TWHDWalletGetAddressDerivation(wallet.get(), TWCoinTypeBitcoin, TWDerivationBitcoinSegwit)); + assertStringsEqual(address, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85"); + } + { + auto address = WRAP(TWString, TWHDWalletGetAddressDerivation(wallet.get(), TWCoinTypeBitcoin, TWDerivationBitcoinLegacy)); + assertStringsEqual(address, "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1"); + } +} + TEST(HDWallet, DeriveEthereum) { auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeEthereum)); From c58d9b0ef7719ef0d351032099be011313207e75 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Fri, 4 Nov 2022 16:12:34 +0100 Subject: [PATCH 035/426] Update in SmartBCH swift test (#2700) --- swift/Tests/Blockchains/SmartBitcoinCashTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swift/Tests/Blockchains/SmartBitcoinCashTests.swift b/swift/Tests/Blockchains/SmartBitcoinCashTests.swift index 131f13d14b6..48d5a6aca65 100644 --- a/swift/Tests/Blockchains/SmartBitcoinCashTests.swift +++ b/swift/Tests/Blockchains/SmartBitcoinCashTests.swift @@ -10,12 +10,12 @@ import XCTest class SmartBitcoinCashTests: XCTestCase { func testAddress() { - let key = PrivateKey(data: Data(hexString: "ab4accc9310d90a61fc354d8f353bca4a2b3c0590685d3eb82d0216af3badddc")!)! + let key = PrivateKey(data: Data(hexString: "155cbd57319f3d938977b4c18000473eb3c432c4e31b667b63e88559c497d82d")!)! let pubkey = key.getPublicKeySecp256k1(compressed: false) let address = AnyAddress(publicKey: pubkey, coin: .smartBitcoinCash) - let addressFromString = AnyAddress(string: "0xA3Dcd899C0f3832DFDFed9479a9d828c6A4EB2A7", coin: .smartBitcoinCash)! + let addressFromString = AnyAddress(string: "0x8bFC9477684987dcAf0970b9bce5E3D9267C99C0", coin: .smartBitcoinCash)! - XCTAssertEqual(pubkey.data.hexString, "0448a9ffac8022f1c7eb5253746e24d11d9b6b2737c0aecd48335feabb95a179916b1f3a97bed6740a85a2d11c663d38566acfb08af48a47ce0c835c65c9b23d0d") + XCTAssertEqual(pubkey.data.hexString, "046439f94100c802691c53ef18523be2c24d301f0e2bd3b425e832378a5405eff4331d5e57303785969073321fc76a8504a3854bdb21e6ab7b268a1737882a29c0") XCTAssertEqual(address.description, addressFromString.description) } From 29e193926a02e073a15669f6b3618416b31a2b70 Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Sat, 5 Nov 2022 10:04:57 +0300 Subject: [PATCH 036/426] [Tools] Kotlin API codegen (#2678) --- tools/android-build | 5 +++++ tools/android-release | 12 ++++++++++++ tools/doxygen_convert_comments | 28 ++++++++++++++++++++++++++++ tools/install-android-dependencies | 29 +++++++++++++++++++++++++++++ tools/kotlin-doc | 27 +++++++++++++++++++++++++++ 5 files changed, 101 insertions(+) create mode 100755 tools/kotlin-doc diff --git a/tools/android-build b/tools/android-build index f63af0c09af..8f87cb4f827 100755 --- a/tools/android-build +++ b/tools/android-build @@ -13,3 +13,8 @@ cp trustwalletcore/build/outputs/aar/trustwalletcore-release.aar ../build/trustw popd echo "Now upload build/trustwalletcore.aar to https://github.com/TrustWallet/trust-wallet-core/releases/tag/$version" + +echo "Building docs..." +tools/kotlin-doc + +echo "Now upload Kotlin docs from build/kdoc.zip to whatever place it needs to be" diff --git a/tools/android-release b/tools/android-release index 60199fb10e5..4856fa5a5ec 100755 --- a/tools/android-release +++ b/tools/android-release @@ -1,6 +1,7 @@ #!/bin/bash # # This script uploads android build to repositories defined in maven-push.gradle +# It also builds and uploads Kotlin docs set -e @@ -14,3 +15,14 @@ pushd android ./gradlew clean build assembleRelease publish -Pversion="$version" echo "Android build uploaded" +popd # android + +echo "Building docs..." +tools/kotlin-doc + +release_url=$(wc_release_url ${version}) +echo "release_url url for docs is $release_url" + +filename=build/dokka/kdoc.zip +download_url=$(wc_upload_asset ${release_url} ${filename}) +echo "download_url is $download_url" diff --git a/tools/doxygen_convert_comments b/tools/doxygen_convert_comments index 91870ffb466..3a81e3baeae 100755 --- a/tools/doxygen_convert_comments +++ b/tools/doxygen_convert_comments @@ -7,6 +7,13 @@ SWIFT_SEE_PATTERN='s/\\see/\- SeeAlso:/g' SWIFT_FOLDER_PATH="swift/Sources/Generated/*.swift" SWIFT_FOLDER_PATH_BAK="swift/Sources/Generated/*.bak" +KOTLIN_PARAMETER_PATTERN='s/\\param/\@param/g' +KOTLIN_RETURN_PATTERN='s/\\return/\@return/g' +KOTLIN_NOTE_PATTERN='s/\\note/\@note/g' +KOTLIN_SEE_PATTERN='s/\\see/\@see/g' +KOTLIN_FOLDER_PATH="jni/java/wallet/core/jni/*.java" +KOTLIN_FOLDER_PATH_BAK="jni/java/wallet/core/jni/*.bak" + function process_swift_comments() { perl -pi.bak -e "$SWIFT_PARAMETER_PATTERN" "$1" perl -pi.bak -e "$SWIFT_RETURN_PATTERN" "$1" @@ -14,6 +21,16 @@ function process_swift_comments() { perl -pi.bak -e "$SWIFT_SEE_PATTERN" "$1" } +function process_kotlin_comments() { + # Process multiline /// comments into javadoc /** ... */ format + perl -0777 -pi.bak -e 's/\/\/\/([^\n]*\n)((?:\ *\/\/\/[^\n]*\n)*)/\/**\n *$1$2*\/\n/g' $1 + perl -pi.bak -e 's/\/\/\//\ \*/g' $1 + + perl -pi.bak -e "$KOTLIN_PARAMETER_PATTERN" "$1" + perl -pi.bak -e "$KOTLIN_RETURN_PATTERN" "$1" + perl -pi.bak -e "$KOTLIN_NOTE_PATTERN" "$1" + perl -pi.bak -e "$KOTLIN_SEE_PATTERN" "$1" +} function swift_convert() { echo "Processing swift conversion" @@ -25,4 +42,15 @@ function swift_convert() { rm -rf $SWIFT_FOLDER_PATH_BAK } +function kotlin_convert() { + echo "Processing kotlin conversion" + + for d in $KOTLIN_FOLDER_PATH ; do + process_kotlin_comments $d + done + + rm -rf $KOTLIN_FOLDER_PATH_BAK +} + swift_convert +kotlin_convert diff --git a/tools/install-android-dependencies b/tools/install-android-dependencies index 7f3d5c64b88..b91ad4d6520 100755 --- a/tools/install-android-dependencies +++ b/tools/install-android-dependencies @@ -2,7 +2,36 @@ set -e +# Dokka stuff +ROOT="$PWD" +DOKKA_CLI_JAR=https://repo1.maven.org/maven2/org/jetbrains/dokka/dokka-cli/1.7.20/dokka-cli-1.7.20.jar + +declare -a DOKKA_DEPS=( +https://repo1.maven.org/maven2/org/jetbrains/dokka/dokka-base/1.7.20/dokka-base-1.7.20.jar +https://repo1.maven.org/maven2/org/jetbrains/dokka/dokka-analysis/1.7.20/dokka-analysis-1.7.20.jar +https://repo1.maven.org/maven2/org/jetbrains/dokka/kotlin-analysis-intellij/1.7.20/kotlin-analysis-intellij-1.7.20.jar +https://repo1.maven.org/maven2/org/jetbrains/dokka/kotlin-analysis-compiler/1.7.20/kotlin-analysis-compiler-1.7.20.jar +https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.6.4/kotlinx-coroutines-core-1.6.4.jar +https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-html-jvm/0.8.0/kotlinx-html-jvm-0.8.0.jar +https://repo1.maven.org/maven2/org/freemarker/freemarker/2.3.31/freemarker-2.3.31.jar +) + $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --verbose "cmake;3.18.1" "ndk;23.1.7779620" $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "system-images;android-26;google_apis;x86" echo -e "y\ny\ny\ny\ny\n" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses + +echo "Downloading Dokka..." +DOKKA_DIR="$ROOT/build/dokka" +mkdir -p "$DOKKA_DIR" +cd "$DOKKA_DIR" + +if [ ! -f dokka-cli-1.7.20.jar ]; then + curl -fSsOL $DOKKA_CLI_JAR +fi + +for dep in "${DOKKA_DEPS[@]}" ; do + if [ ! -f ${dep##*/} ]; then + curl -fSsOL $dep + fi +done diff --git a/tools/kotlin-doc b/tools/kotlin-doc new file mode 100755 index 00000000000..09105cb56a6 --- /dev/null +++ b/tools/kotlin-doc @@ -0,0 +1,27 @@ +#!/bin/bash + +pushd jni + +DOKKA_CLI_JAR=../build/dokka/dokka-cli-1.7.20.jar + +declare -a DOKKA_DEPS=( +dokka-base-1.7.20.jar +dokka-analysis-1.7.20.jar +kotlin-analysis-intellij-1.7.20.jar +kotlin-analysis-compiler-1.7.20.jar +kotlinx-coroutines-core-1.6.4.jar +kotlinx-html-jvm-0.8.0.jar +freemarker-2.3.31.jar +) + +pluginClassPath="" +for dep in "${DOKKA_DEPS[@]}" ; do + pluginClassPath="$pluginClassPath;../build/dokka/$dep" +done + +java -jar ${DOKKA_CLI_JAR} -outputDir dokka-out -pluginsClasspath $pluginClassPath -sourceSet "-sourceSetName TrustWallet -src java/wallet/core/jni" + +popd #jni + +zip -rq kdoc.zip jni/dokka-out +mv kdoc.zip build/dokka From cc07e5f30bae8e78914211f5f2b56d502ad124ad Mon Sep 17 00:00:00 2001 From: hewigovens <360470+hewigovens@users.noreply.github.com> Date: Sun, 6 Nov 2022 17:43:32 +0900 Subject: [PATCH 037/426] [CI] Test Kotlin doc (#2705) * Fix dokka-out missing, run kotlin-doc on CI * Fix default root module name and extra jni path in zip --- .github/workflows/android-ci.yml | 25 ++++++++++++++++--------- .gitignore | 1 + tools/kotlin-doc | 9 +++++---- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 0bb3ed90964..3dfe3e5b9f0 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -17,28 +17,35 @@ jobs: uses: actions/setup-java@v1 with: java-version: 11 + - name: Install system dependencies run: brew install boost ninja + - name: Install Android Dependencies - run: | - tools/install-android-dependencies + run: tools/install-android-dependencies + - name: Cache internal dependencies id: internal_cache uses: actions/cache@v1.1.2 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + - name: Install internal dependencies - run: | - tools/install-dependencies + run: tools/install-dependencies if: steps.internal_cache.outputs.cache-hit != 'true' + + - name: Generate files + run: tools/generate-files + + - name: Build Kotlin doc + run: tools/kotlin-doc + - name: Run test - run: | - tools/generate-files - tools/android-test + run: tools/android-test + - name: Build sample app - run: | - tools/samples-build android + run: tools/samples-build android env: GITHUB_USER: ${{ github.actor }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 707a2517dee..4c0f5e373aa 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ lib/protobuf jni/cpp/generated jni/java/wallet/core/jni jni/java/wallet/core/proto +jni/dokka-out swift/Sources/Generated swift/wallet-core/ src/Generated/*.cpp diff --git a/tools/kotlin-doc b/tools/kotlin-doc index 09105cb56a6..d1ea9e5de3b 100755 --- a/tools/kotlin-doc +++ b/tools/kotlin-doc @@ -19,9 +19,10 @@ for dep in "${DOKKA_DEPS[@]}" ; do pluginClassPath="$pluginClassPath;../build/dokka/$dep" done -java -jar ${DOKKA_CLI_JAR} -outputDir dokka-out -pluginsClasspath $pluginClassPath -sourceSet "-sourceSetName TrustWallet -src java/wallet/core/jni" +mkdir -p dokka-out -popd #jni +java -jar ${DOKKA_CLI_JAR} -moduleName WalletCore -outputDir dokka-out -pluginsClasspath $pluginClassPath -sourceSet "-sourceSetName TrustWallet -src java/wallet/core/jni" + +zip -rq kdoc.zip dokka-out && mv kdoc.zip ../build/dokka -zip -rq kdoc.zip jni/dokka-out -mv kdoc.zip build/dokka +popd #jni From b59b97c7684d77e8709e82934a0ea2a8a26db41b Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 8 Nov 2022 08:57:46 +0100 Subject: [PATCH 038/426] Add Hedera support (#2680) --- CMakeLists.txt | 2 +- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../blockchains/hedera/TestHederaAddress.kt | 35 + .../blockchains/hedera/TestHederaSigner.kt | 72 + codegen/lib/templates/newcoin/Address.h.erb | 2 +- codegen/lib/templates/newcoin/Entry.h.erb | 2 +- docs/registry.md | 1 + include/TrustWalletCore/TWBlockchain.h | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 41 +- src/Coin.cpp | 3 + src/Data.h | 6 + src/Hedera/Address.cpp | 90 + src/Hedera/Address.h | 52 + src/Hedera/DER.cpp | 20 + src/Hedera/DER.h | 18 + src/Hedera/Entry.cpp | 26 + src/Hedera/Entry.h | 22 + src/Hedera/Protobuf/.gitignore | 3 + src/Hedera/Protobuf/basic_types.proto | 1596 +++++++++++++++++ src/Hedera/Protobuf/crypto_transfer.proto | 55 + src/Hedera/Protobuf/duration.proto | 38 + src/Hedera/Protobuf/timestamp.proto | 54 + src/Hedera/Protobuf/transaction_body.proto | 76 + .../Protobuf/transaction_contents.proto | 42 + src/Hedera/Signer.cpp | 107 ++ src/Hedera/Signer.h | 25 + src/algorithm/string.hpp | 27 + src/proto/Hedera.proto | 98 + swift/Tests/Blockchains/HederaTests.swift | 59 + swift/Tests/CoinAddressDerivationTests.swift | 4 + tests/chains/Hedera/AddressTests.cpp | 58 + tests/chains/Hedera/SignerTests.cpp | 238 +++ tests/chains/Hedera/TWAnySignerTests.cpp | 42 + tests/chains/Hedera/TWCoinTypeTests.cpp | 33 + tests/common/CoinAddressDerivationTests.cpp | 3 + tests/common/HDWallet/HDWalletTests.cpp | 23 + tests/common/algorithm/string.cpp | 19 + tools/generate-files | 1 + wasm/CMakeLists.txt | 4 +- wasm/tests/Blockchain/Hedera.test.ts | 62 + 41 files changed, 3050 insertions(+), 12 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaSigner.kt create mode 100644 src/Hedera/Address.cpp create mode 100644 src/Hedera/Address.h create mode 100644 src/Hedera/DER.cpp create mode 100644 src/Hedera/DER.h create mode 100644 src/Hedera/Entry.cpp create mode 100644 src/Hedera/Entry.h create mode 100644 src/Hedera/Protobuf/.gitignore create mode 100644 src/Hedera/Protobuf/basic_types.proto create mode 100644 src/Hedera/Protobuf/crypto_transfer.proto create mode 100644 src/Hedera/Protobuf/duration.proto create mode 100644 src/Hedera/Protobuf/timestamp.proto create mode 100644 src/Hedera/Protobuf/transaction_body.proto create mode 100644 src/Hedera/Protobuf/transaction_contents.proto create mode 100644 src/Hedera/Signer.cpp create mode 100644 src/Hedera/Signer.h create mode 100644 src/algorithm/string.hpp create mode 100644 src/proto/Hedera.proto create mode 100644 swift/Tests/Blockchains/HederaTests.swift create mode 100644 tests/chains/Hedera/AddressTests.cpp create mode 100644 tests/chains/Hedera/SignerTests.cpp create mode 100644 tests/chains/Hedera/TWAnySignerTests.cpp create mode 100644 tests/chains/Hedera/TWCoinTypeTests.cpp create mode 100644 tests/common/algorithm/string.cpp create mode 100644 wasm/tests/Blockchain/Hedera.test.ts diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b061e1c15b..ed02ed918ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,7 +97,7 @@ endif () if (NOT ANDROID AND TW_UNITY_BUILD) set_target_properties(TrustWalletCore PROPERTIES UNITY_BUILD ON) - file(GLOB_RECURSE PROTOBUF_SOURCE_FILES CONFIGURE_DEPENDS src/Cosmos/Protobuf/*.pb.cc src/proto/*.pb.cc) + file(GLOB_RECURSE PROTOBUF_SOURCE_FILES CONFIGURE_DEPENDS src/Cosmos/Protobuf/*.pb.cc src/Hedera/Protobuf/*.pb.cc src/proto/*.pb.cc) foreach(file ${PROTOBUF_SOURCE_FILES}) set_property(SOURCE ${file} PROPERTY SKIP_UNITY_BUILD_INCLUSION ON) endforeach() 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 98f69f55d1b..ddd2c1dd86c 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 @@ -106,5 +106,6 @@ class CoinAddressDerivationTests { NERVOS -> assertEquals("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3", address) EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) + HEDERA -> assertEquals("0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaAddress.kt new file mode 100644 index 00000000000..be92015f825 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaAddress.kt @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 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.hedera + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestHederaAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val any = AnyAddress("0.0.1377988", CoinType.HEDERA) + assertEquals(any.coin(), CoinType.HEDERA) + assertEquals(any.description(), "0.0.1377988") + + Assert.assertFalse( + AnyAddress.isValid( + "0.0.a", + CoinType.HEDERA + ) + ) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaSigner.kt new file mode 100644 index 00000000000..dec11a37bcb --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaSigner.kt @@ -0,0 +1,72 @@ +// Copyright © 2017-2022 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.hedera + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHexBytesInByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.* +import wallet.core.jni.proto.Hedera + +class TestHederaSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun HederaTransactionSimpleTransferSigning() { + // Successfully broadcasted: https://hashscan.io/testnet/transaction/0.0.48694347-1667222879-749068449?t=1667222891.440398729&p=1 + val key = + "e87a5584c0173263e138db689fdb2a7389025aaae7cb1a18a1017d76012130e8".toHexBytesInByteString() + + val transfer = Hedera.TransferMessage + .newBuilder() + .setAmount(100000000) + .setFrom("0.0.48694347") + .setTo("0.0.48462050") + .build() + + val timestamp = Hedera.Timestamp + .newBuilder() + .setSeconds(1667222879) + .setNanos(749068449) + .build() + + val transactionID = Hedera.TransactionID + .newBuilder() + .setTransactionValidStart(timestamp) + .setAccountID("0.0.48694347") + .build() + + val body = Hedera.TransactionBody + .newBuilder() + .setMemo("") + .setTransfer(transfer) + .setTransactionID(transactionID) + .setNodeAccountID("0.0.9") + .setTransactionFee(100000000) + .setTransactionValidDuration(120) + .build() + + val signingInput = Hedera.SigningInput + .newBuilder() + .setPrivateKey(key) + .setBody(body).build() + + val result = AnySigner.sign(signingInput, CoinType.HEDERA, Hedera.SigningOutput.parser()) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.encoded.toByteArray())), + "0a440a150a0c08df9aff9a0610a1c197e502120518cb889c17120218091880c2d72f22020878721e0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f12660a640a205d3a70d08b2beafb72c7a68986b3ff819a306078b8c359d739e4966e82e6d40e1a40612589c3b15f1e3ed6084b5a3a5b1b81751578cac8d6c922f31731b3982a5bac80a22558b2197276f5bae49b62503a4d39448ceddbc5ef3ba9bee4c0f302f70c" + ) + } +} diff --git a/codegen/lib/templates/newcoin/Address.h.erb b/codegen/lib/templates/newcoin/Address.h.erb index 74997f6330c..d39dad53723 100644 --- a/codegen/lib/templates/newcoin/Address.h.erb +++ b/codegen/lib/templates/newcoin/Address.h.erb @@ -7,7 +7,7 @@ #pragma once #include "Data.h" -#include "../PublicKey.h" +#include "PublicKey.h" #include diff --git a/codegen/lib/templates/newcoin/Entry.h.erb b/codegen/lib/templates/newcoin/Entry.h.erb index fa115a9a30b..090d5d06445 100644 --- a/codegen/lib/templates/newcoin/Entry.h.erb +++ b/codegen/lib/templates/newcoin/Entry.h.erb @@ -14,7 +14,7 @@ namespace TW::<%= format_name(coin) %> { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, const PrefixVariant& addressPrefix) 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; // normalizeAddress(): implement this if needed, e.g. Ethereum address is EIP55 checksummed diff --git a/docs/registry.md b/docs/registry.md index 5d89f556a91..3db48715d95 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -67,6 +67,7 @@ This list is generated from [./registry.json](../registry.json) | 1815 | Cardano | ADA | | | | 2301 | Qtum | QTUM | | | | 2718 | Nebulas | NAS | | | +| 3030 | Hedera | HBAR | | | | 6060 | GoChain | GO | | | | 8964 | NULS | NULS | | | | 18000 | Meter | MTR | | | diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 80fe59660ce..44724e534f6 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -55,6 +55,7 @@ enum TWBlockchain { TWBlockchainNervos = 41, TWBlockchainEverscale = 42, TWBlockchainAptos = 43, // Aptos + TWBlockchainHedera = 44, // Hedera }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 4c009702001..d740aa310f5 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -121,6 +121,7 @@ enum TWCoinType { TWCoinTypeNervos = 309, TWCoinTypeEverscale = 396, TWCoinTypeAptos = 637, + TWCoinTypeHedera = 3030, }; /// Returns the blockchain for a coin type. diff --git a/registry.json b/registry.json index 96dedfb41e8..fe76a261485 100644 --- a/registry.json +++ b/registry.json @@ -1944,7 +1944,7 @@ "documentation": "https://eth.wiki/json-rpc/API" }, "deprecated": true, - "testFolderName" : "Binance" + "testFolderName": "Binance" }, { "id": "smartchain", @@ -1977,7 +1977,7 @@ "rpc": "https://bsc-dataseed1.binance.org", "documentation": "https://eth.wiki/json-rpc/API" }, - "testFolderName" : "Binance" + "testFolderName": "Binance" }, { "id": "polygon", @@ -2157,7 +2157,7 @@ "rpc": "https://http-mainnet-node.huobichain.com", "documentation": "https://eth.wiki/json-rpc/API" }, - "testFolderName" : "ECO" + "testFolderName": "ECO" }, { "id": "avalanchec", @@ -2188,7 +2188,7 @@ "clientPublic": "https://api.avax.network/ext/bc/C/rpc", "clientDocs": "https://docs.avax.network/" }, - "testFolderName" : "Avalanche" + "testFolderName": "Avalanche" }, { "id": "xdai", @@ -2436,7 +2436,7 @@ "clientPublic": "https://evm-cronos.crypto.org", "clientDocs": "https://eth.wiki/json-rpc/API" }, - "testFolderName" : "Cronos" + "testFolderName": "Cronos" }, { "id": "kavaevm", @@ -2495,7 +2495,7 @@ "rpc": "https://smartbch.fountainhead.cash/mainnet", "documentation": "https://github.com/smartbch/docs/blob/main/developers-guide/jsonrpc.md" }, - "testFolderName" : "Bitcoin" + "testFolderName": "Bitcoin" }, { "id": "kcc", @@ -2526,7 +2526,7 @@ "rpc": "https://rpc-mainnet.kcc.network", "documentation": "https://docs.kcc.io/#/en-us/" }, - "testFolderName" : "KuCoinCommunityChain" + "testFolderName": "KuCoinCommunityChain" }, { "id": "boba", @@ -2821,5 +2821,32 @@ "rpc": "https://exchainrpc.okex.org", "documentation": "https://okc-docs.readthedocs.io/en/latest" } + }, + { + "id": "hedera", + "name": "Hedera", + "coinId": 3030, + "symbol": "HBAR", + "decimals": 8, + "blockchain": "Hedera", + "derivation": [ + { + "path": "m/44'/3030'/0'/0'/0" + } + ], + "curve": "ed25519", + "publicKeyType": "ed25519", + "explorer": { + "url": "https://hashscan.io/mainnet", + "txPath": "/transaction/", + "accountPath": "/account/", + "sampleTx": "0.0.19790-1666769504-858851231", + "sampleAccount": "0.0.19790" + }, + "info": { + "url": "https://hedera.com/", + "source": "https://github.com/hashgraph", + "documentation": "https://docs.hedera.com" + } } ] diff --git a/src/Coin.cpp b/src/Coin.cpp index 9b88d7eb252..c99c086c8bc 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -55,6 +55,7 @@ #include "XRP/Entry.h" #include "Zcash/Entry.h" #include "Zilliqa/Entry.h" +#include "Hedera/Entry.h" // end_of_coin_includes_marker_do_not_modify using namespace TW; @@ -103,6 +104,7 @@ Zcash::Entry zcashDP; Zilliqa::Entry zilliqaDP; Nervos::Entry NervosDP; Everscale::Entry EverscaleDP; +Hedera::Entry HederaDP; // end_of_coin_dipatcher_declarations_marker_do_not_modify CoinEntry* coinDispatcher(TWCoinType coinType) { @@ -153,6 +155,7 @@ CoinEntry* coinDispatcher(TWCoinType coinType) { case TWBlockchainNervos: entry = &NervosDP; break; case TWBlockchainEverscale: entry = &EverscaleDP; break; case TWBlockchainAptos: entry = &AptosDP; break; + case TWBlockchainHedera: entry = &HederaDP; break; // end_of_coin_dipatcher_switch_marker_do_not_modify default: entry = nullptr; break; diff --git a/src/Data.h b/src/Data.h index 1c43fdc68b0..6ab74ccaba9 100644 --- a/src/Data.h +++ b/src/Data.h @@ -32,6 +32,12 @@ inline void append(Data& data, const Data& suffix) { data.insert(data.end(), suffix.begin(), suffix.end()); } +inline Data concat(const Data& data, const Data& suffix) { + Data out = data; + append(out, suffix); + return out; +} + inline void append(Data& data, const byte suffix) { data.push_back(suffix); } diff --git a/src/Hedera/Address.cpp b/src/Hedera/Address.cpp new file mode 100644 index 00000000000..c3129a286cb --- /dev/null +++ b/src/Hedera/Address.cpp @@ -0,0 +1,90 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "DER.h" +#include "algorithm/string.hpp" + +#include +#include + +namespace TW::Hedera::internal { + static const std::regex gEntityIDRegex{"(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([a-z]{5}))?$"}; +} + +namespace TW::Hedera { + + +Alias::Alias(std::optional alias) noexcept : mPubKey(std::move(alias)) { + +} + +std::string Alias::string() const noexcept { + std::string pubkeyBytes = ""; + if (mPubKey.has_value()) { + pubkeyBytes = hex(mPubKey.value().bytes); + } + return gHederaDerPrefixPublic + pubkeyBytes; +} + +bool Address::isValid(const std::string& string) { + using namespace internal; + std::smatch match; + auto isValid = std::regex_match(string, match, gEntityIDRegex); + if (!isValid) { + auto parts = TW::ssplit(string, '.'); + if (parts.size() != 3) { + return false; + } + auto isNumberFunctor = [](std::string_view input) { + return input.find_first_not_of("0123456789") == std::string::npos; + }; + if (!isNumberFunctor(parts[0]) || !isNumberFunctor(parts[1])) { + return false; + } + isValid = hasDerPrefix(parts[2]); + } + return isValid; +} + +Address::Address(const std::string& string) { + if (!isValid(string)) { + throw std::invalid_argument("Invalid address string"); + } + + auto toInt = [](std::string_view s) -> std::optional { + if (std::size_t value = 0; std::from_chars(s.begin(), s.end(), value).ec == std::errc{}) { + return value; + } else { + return std::nullopt; + } + }; + + // When creating an Address by string - we assume to only sent to 0.0.1 format, alias is internal. + auto parts = TW::ssplit(string, '.'); + mShard = *toInt(parts[0]); + mRealm = *toInt(parts[1]); + mNum = *toInt(parts[2]); +} + +Address::Address(const PublicKey& publicKey) + : Address(0, 0, 0, publicKey) { +} + +std::string Address::string() const { + std::string out = std::to_string(mShard) + "." + std::to_string(mRealm) + "."; + if (mAlias.mPubKey.has_value()) { + return out + mAlias.string(); + } + return out + std::to_string(mNum); +} + +Address::Address(std::size_t shard, std::size_t realm, std::size_t num, std::optional alias) + : mShard(shard), mRealm(realm), mNum(num), mAlias(std::move(alias)) { +} + +} // namespace TW::Hedera diff --git a/src/Hedera/Address.h b/src/Hedera/Address.h new file mode 100644 index 00000000000..ab29025aea1 --- /dev/null +++ b/src/Hedera/Address.h @@ -0,0 +1,52 @@ +// Copyright © 2017-2022 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 +#include + +namespace TW::Hedera { + +struct Alias { + explicit Alias(std::optional alias = std::nullopt) noexcept; + std::string string() const noexcept; + std::optional mPubKey{std::nullopt}; +}; + +class Address { +public: + /// Determines whether a string makes a valid address. + static bool isValid(const std::string& string); + + /// Initializes a Hedera address with a string representation. + explicit Address(const std::string& string); + + /// Initializes a Hedera address with a public key. + explicit Address(const PublicKey& publicKey); + + /// Initializes a Hedera address with a shard, realm, num and optional alias + explicit Address(std::size_t shard, std::size_t realm, std::size_t num, std::optional alias = std::nullopt); + + /// Returns a string representation of the address. + std::string string() const; + + std::size_t shard() const { return mShard; } + std::size_t realm() const { return mRealm; } + std::size_t num() const { return mNum; } + const Alias& alias() const {return mAlias;} + +private: + std::size_t mShard{0}; + std::size_t mRealm{0}; + std::size_t mNum; + Alias mAlias; +}; + +} // namespace TW::Hedera diff --git a/src/Hedera/DER.cpp b/src/Hedera/DER.cpp new file mode 100644 index 00000000000..5da801ee061 --- /dev/null +++ b/src/Hedera/DER.cpp @@ -0,0 +1,20 @@ +// Copyright © 2017-2022 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 "DER.h" +#include "PublicKey.h" +#include "HexCoding.h" + +namespace TW::Hedera { + +bool hasDerPrefix(const std::string& input) noexcept { + if (std::size_t pos = input.find(gHederaDerPrefixPublic); pos != std::string::npos) { + return PublicKey::isValid(parse_hex(input.substr(pos + std::string(gHederaDerPrefixPublic).size())), TWPublicKeyTypeED25519); + } + return false; +} + +} // namespace TW::Hedera diff --git a/src/Hedera/DER.h b/src/Hedera/DER.h new file mode 100644 index 00000000000..67bf784d71e --- /dev/null +++ b/src/Hedera/DER.h @@ -0,0 +1,18 @@ +// Copyright © 2017-2022 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 + +namespace TW::Hedera { + // Hedera DER prefix: https://github.com/hashgraph/hedera-sdk-js/blob/e0cd39c84ab189d59a6bcedcf16e4102d7bb8beb/packages/cryptography/src/Ed25519PrivateKey.js#L8 + inline constexpr const char* const gHederaDerPrefixPrivate = "302e020100300506032b657004220420"; + // Hedera DER prefix: https://github.com/hashgraph/hedera-sdk-js/blob/e0cd39c84ab189d59a6bcedcf16e4102d7bb8beb/packages/cryptography/src/Ed25519PublicKey.js#L7 + inline constexpr const char* const gHederaDerPrefixPublic = "302a300506032b6570032100"; + + bool hasDerPrefix(const std::string& input) noexcept; +} diff --git a/src/Hedera/Entry.cpp b/src/Hedera/Entry.cpp new file mode 100644 index 00000000000..60dfc7e124a --- /dev/null +++ b/src/Hedera/Entry.cpp @@ -0,0 +1,26 @@ +// Copyright © 2017-2022 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" + +namespace TW::Hedera { + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return Address::isValid(address); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { + return Address(publicKey).string(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +} // namespace TW::Hedera diff --git a/src/Hedera/Entry.h b/src/Hedera/Entry.h new file mode 100644 index 00000000000..5cf496ef185 --- /dev/null +++ b/src/Hedera/Entry.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 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::Hedera { + +/// Entry point for implementation of Hedera coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry final : public CoinEntry { +public: + virtual bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) 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; +}; + +} // namespace TW::Hedera diff --git a/src/Hedera/Protobuf/.gitignore b/src/Hedera/Protobuf/.gitignore new file mode 100644 index 00000000000..c96d61208c0 --- /dev/null +++ b/src/Hedera/Protobuf/.gitignore @@ -0,0 +1,3 @@ +*.cc +*.h + diff --git a/src/Hedera/Protobuf/basic_types.proto b/src/Hedera/Protobuf/basic_types.proto new file mode 100644 index 00000000000..40bb309ce34 --- /dev/null +++ b/src/Hedera/Protobuf/basic_types.proto @@ -0,0 +1,1596 @@ +// Taken from https://github.com/hashgraph/hedera-protobufs/tree/main/services + +syntax = "proto3"; + +package proto; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2022 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +import "timestamp.proto"; +import "google/protobuf/wrappers.proto"; + +option java_package = "com.hederahashgraph.api.proto.java"; +option java_multiple_files = true; + +/** + * Each shard has a nonnegative shard number. Each realm within a given shard has a nonnegative + * realm number (that number might be reused in other shards). And each account, file, and smart + * contract instance within a given realm has a nonnegative number (which might be reused in other + * realms). Every account, file, and smart contract instance is within exactly one realm. So a + * FileID is a triplet of numbers, like 0.1.2 for entity number 2 within realm 1 within shard 0. + * Each realm maintains a single counter for assigning numbers, so if there is a file with ID + * 0.1.2, then there won't be an account or smart contract instance with ID 0.1.2. + * + * Everything is partitioned into realms so that each Solidity smart contract can access everything + * in just a single realm, locking all those entities while it's running, but other smart contracts + * could potentially run in other realms in parallel. So realms allow Solidity to be parallelized + * somewhat, even though the language itself assumes everything is serial. + */ +message ShardID { + /** + * the shard number (nonnegative) + */ + int64 shardNum = 1; +} + +/** + * The ID for a realm. Within a given shard, every realm has a unique ID. Each account, file, and + * contract instance belongs to exactly one realm. + */ +message RealmID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; +} + +/** + * The ID for an a cryptocurrency account + */ +message AccountID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; + + /** + * The account number unique within its realm which can be either a non-negative integer or an alias public key. + * For any AccountID fields in the query response, transaction record or transaction receipt only accountNum will + * be populated. + */ + oneof account { + /** + * A non-negative account number unique within its realm + */ + int64 accountNum = 3; + + /** + * The public key bytes to be used as the account's alias. The public key bytes are the result of serializing + * a protobuf Key message for any primitive key type. Currently only primitive key bytes are supported as an alias + * (ThresholdKey, KeyList, ContractID, and delegatable_contract_id are not supported) + * + * May also be the ethereum account 20-byte EVM address to be used initially in place of the public key bytes. This EVM + * address may be either the encoded form of the shard.realm.num or the keccak-256 hash of a ECDSA_SECP256K1 primitive key. + * + * At most one account can ever have a given alias and it is used for account creation if it + * was automatically created using a crypto transfer. It will be null if an account is created normally. + * It is immutable once it is set for an account. + * + * If a transaction auto-creates the account, any further transfers to that alias will simply be deposited + * in that account, without creating anything, and with no creation fee being charged. + * + * If a transaction lazily-creates this account, a subsequent transaction will be required containing the public key bytes + * that map to the EVM address bytes. The provided public key bytes will then serve as the final alias bytes. + */ + bytes alias = 4; + + } +} + +/** + * The ID for a file + */ +message FileID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; + + /** + * A nonnegative File number unique within its realm + */ + int64 fileNum = 3; +} + +/** + * The ID for a smart contract instance + */ +message ContractID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; + + oneof contract { + /** + * A nonnegative number unique within a given shard and realm + */ + int64 contractNum = 3; + + /** + * The 20-byte EVM address of the contract to call. + * + * Every contract has an EVM address determined by its shard.realm.num id. + * This address is as follows: + *
    + *
  1. The first 4 bytes are the big-endian representation of the shard.
  2. + *
  3. The next 8 bytes are the big-endian representation of the realm.
  4. + *
  5. The final 8 bytes are the big-endian representation of the number.
  6. + *
+ * + * Contracts created via CREATE2 have an additional, primary address that is + * derived from the EIP-1014 + * specification, and does not have a simple relation to a shard.realm.num id. + * + * (Please do note that CREATE2 contracts can also be referenced by the three-part + * EVM address described above.) + */ + bytes evm_address = 4; + } +} + +/** + * The ID for a transaction. This is used for retrieving receipts and records for a transaction, for + * appending to a file right after creating it, for instantiating a smart contract with bytecode in + * a file just created, and internally by the network for detecting when duplicate transactions are + * submitted. A user might get a transaction processed faster by submitting it to N nodes, each with + * a different node account, but all with the same TransactionID. Then, the transaction will take + * effect when the first of all those nodes submits the transaction and it reaches consensus. The + * other transactions will not take effect. So this could make the transaction take effect faster, + * if any given node might be slow. However, the full transaction fee is charged for each + * transaction, so the total fee is N times as much if the transaction is sent to N nodes. + * + * Applicable to Scheduled Transactions: + * - The ID of a Scheduled Transaction has transactionValidStart and accountIDs inherited from the + * ScheduleCreate transaction that created it. That is to say that they are equal + * - The scheduled property is true for Scheduled Transactions + * - transactionValidStart, accountID and scheduled properties should be omitted + */ +message TransactionID { + /** + * The transaction is invalid if consensusTimestamp < transactionID.transactionStartValid + */ + Timestamp transactionValidStart = 1; + + /** + * The Account ID that paid for this transaction + */ + AccountID accountID = 2; + + /** + * Whether the Transaction is of type Scheduled or no + */ + bool scheduled = 3; + + /** + * The identifier for an internal transaction that was spawned as part + * of handling a user transaction. (These internal transactions share the + * transactionValidStart and accountID of the user transaction, so a + * nonce is necessary to give them a unique TransactionID.) + * + * An example is when a "parent" ContractCreate or ContractCall transaction + * calls one or more HTS precompiled contracts; each of the "child" + * transactions spawned for a precompile has a id with a different nonce. + */ + int32 nonce = 4; +} + +/** + * An account, and the amount that it sends or receives during a cryptocurrency or token transfer. + */ +message AccountAmount { + /** + * The Account ID that sends/receives cryptocurrency or tokens + */ + AccountID accountID = 1; + + /** + * The amount of tinybars (for Crypto transfers) or in the lowest + * denomination (for Token transfers) that the account sends(negative) or + * receives(positive) + */ + sint64 amount = 2; + + /** + * If true then the transfer is expected to be an approved allowance and the + * accountID is expected to be the owner. The default is false (omitted). + */ + bool is_approval = 3; +} + +/** + * A list of accounts and amounts to transfer out of each account (negative) or into it (positive). + */ +message TransferList { + /** + * Multiple list of AccountAmount pairs, each of which has an account and + * an amount to transfer into it (positive) or out of it (negative) + */ + repeated AccountAmount accountAmounts = 1; +} + +/** + * A sender account, a receiver account, and the serial number of an NFT of a Token with + * NON_FUNGIBLE_UNIQUE type. When minting NFTs the sender will be the default AccountID instance + * (0.0.0) and when burning NFTs, the receiver will be the default AccountID instance. + */ +message NftTransfer { + /** + * The accountID of the sender + */ + AccountID senderAccountID = 1; + + /** + * The accountID of the receiver + */ + AccountID receiverAccountID = 2; + + /** + * The serial number of the NFT + */ + int64 serialNumber = 3; + + /** + * If true then the transfer is expected to be an approved allowance and the + * senderAccountID is expected to be the owner. The default is false (omitted). + */ + bool is_approval = 4; +} + +/** + * A list of token IDs and amounts representing the transferred out (negative) or into (positive) + * amounts, represented in the lowest denomination of the token + */ +message TokenTransferList { + /** + * The ID of the token + */ + TokenID token = 1; + + /** + * Applicable to tokens of type FUNGIBLE_COMMON. Multiple list of AccountAmounts, each of which + * has an account and amount + */ + repeated AccountAmount transfers = 2; + + /** + * Applicable to tokens of type NON_FUNGIBLE_UNIQUE. Multiple list of NftTransfers, each of + * which has a sender and receiver account, including the serial number of the NFT + */ + repeated NftTransfer nftTransfers = 3; + + /** + * If present, the number of decimals this fungible token type is expected to have. The transfer + * will fail with UNEXPECTED_TOKEN_DECIMALS if the actual decimals differ. + */ + google.protobuf.UInt32Value expected_decimals = 4; +} + +/** + * A rational number, used to set the amount of a value transfer to collect as a custom fee + */ +message Fraction { + /** + * The rational's numerator + */ + int64 numerator = 1; + + /** + * The rational's denominator; a zero value will result in FRACTION_DIVIDES_BY_ZERO + */ + int64 denominator = 2; +} + +/** + * Unique identifier for a topic (used by the consensus service) + */ +message TopicID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; + + /** + * Unique topic identifier within a realm (nonnegative). + */ + int64 topicNum = 3; +} + +/** + * Unique identifier for a token + */ +message TokenID { + /** + * A nonnegative shard number + */ + int64 shardNum = 1; + + /** + * A nonnegative realm number + */ + int64 realmNum = 2; + + /** + * A nonnegative token number + */ + int64 tokenNum = 3; +} + +/** + * Unique identifier for a Schedule + */ +message ScheduleID { + /** + * A nonnegative shard number + */ + int64 shardNum = 1; + + /** + * A nonnegative realm number + */ + int64 realmNum = 2; + + /** + * A nonnegative schedule number + */ + int64 scheduleNum = 3; +} + +/** + * Possible Token Types (IWA Compatibility). + * Apart from fungible and non-fungible, Tokens can have either a common or unique representation. + * This distinction might seem subtle, but it is important when considering how tokens can be traced + * and if they can have isolated and unique properties. + */ +enum TokenType { + /** + * Interchangeable value with one another, where any quantity of them has the same value as + * another equal quantity if they are in the same class. Share a single set of properties, not + * distinct from one another. Simply represented as a balance or quantity to a given Hedera + * account. + */ + FUNGIBLE_COMMON = 0; + + /** + * Unique, not interchangeable with other tokens of the same type as they typically have + * different values. Individually traced and can carry unique properties (e.g. serial number). + */ + NON_FUNGIBLE_UNIQUE = 1; +} + +/** + * Allows a set of resource prices to be scoped to a certain type of a HAPI operation. + * + * For example, the resource prices for a TokenMint operation are different between minting fungible + * and non-fungible tokens. This enum allows us to "mark" a set of prices as applying to one or the + * other. + * + * Similarly, the resource prices for a basic TokenCreate without a custom fee schedule yield a + * total price of $1. The resource prices for a TokenCreate with a custom fee schedule are different + * and yield a total base price of $2. + */ +enum SubType { + /** + * The resource prices have no special scope + */ + DEFAULT = 0; + + /** + * The resource prices are scoped to an operation on a fungible common token + */ + TOKEN_FUNGIBLE_COMMON = 1; + + /** + * The resource prices are scoped to an operation on a non-fungible unique token + */ + TOKEN_NON_FUNGIBLE_UNIQUE = 2; + + /** + * The resource prices are scoped to an operation on a fungible common + * token with a custom fee schedule + */ + TOKEN_FUNGIBLE_COMMON_WITH_CUSTOM_FEES = 3; + + /** + * The resource prices are scoped to an operation on a non-fungible unique + * token with a custom fee schedule + */ + TOKEN_NON_FUNGIBLE_UNIQUE_WITH_CUSTOM_FEES = 4; + + /** + * The resource prices are scoped to a ScheduleCreate containing a ContractCall. + */ + SCHEDULE_CREATE_CONTRACT_CALL = 5; +} + +/** + * Possible Token Supply Types (IWA Compatibility). + * Indicates how many tokens can have during its lifetime. + */ +enum TokenSupplyType { + /** + * Indicates that tokens of that type have an upper bound of Long.MAX_VALUE. + */ + INFINITE = 0; + + /** + * Indicates that tokens of that type have an upper bound of maxSupply, + * provided on token creation. + */ + FINITE = 1; +} + +/** + * Possible Freeze statuses returned on TokenGetInfoQuery or CryptoGetInfoResponse in + * TokenRelationship + */ +enum TokenFreezeStatus { + /** + * UNDOCUMENTED + */ + FreezeNotApplicable = 0; + + /** + * UNDOCUMENTED + */ + Frozen = 1; + + /** + * UNDOCUMENTED + */ + Unfrozen = 2; +} + +/** + * Possible KYC statuses returned on TokenGetInfoQuery or CryptoGetInfoResponse in TokenRelationship + */ +enum TokenKycStatus { + /** + * UNDOCUMENTED + */ + KycNotApplicable = 0; + + /** + * UNDOCUMENTED + */ + Granted = 1; + + /** + * UNDOCUMENTED + */ + Revoked = 2; +} + +/** + * Possible Pause statuses returned on TokenGetInfoQuery + */ +enum TokenPauseStatus { + /** + * Indicates that a Token has no pauseKey + */ + PauseNotApplicable = 0; + + /** + * Indicates that a Token is Paused + */ + Paused = 1; + + /** + * Indicates that a Token is Unpaused. + */ + Unpaused = 2; +} + +/** + * A Key can be a public key from either the Ed25519 or ECDSA(secp256k1) signature schemes, where + * in the ECDSA(secp256k1) case we require the 33-byte compressed form of the public key. We call + * these public keys primitive keys. + * + * If an account has primitive key associated to it, then the corresponding private key must sign + * any transaction to transfer cryptocurrency out of it. + * + * A Key can also be the ID of a smart contract instance, which is then authorized to perform any + * precompiled contract action that requires this key to sign. + * + * Note that when a Key is a smart contract ID, it doesn't mean the contract with that ID + * will actually create a cryptographic signature. It only means that when the contract calls a + * precompiled contract, the resulting "child transaction" will be authorized to perform any action + * controlled by the Key. + * + * A Key can be a "threshold key", which means a list of M keys, any N of which must sign in order + * for the threshold signature to be considered valid. The keys within a threshold signature may + * themselves be threshold signatures, to allow complex signature requirements. + * + * A Key can be a "key list" where all keys in the list must sign unless specified otherwise in the + * documentation for a specific transaction type (e.g. FileDeleteTransactionBody). Their use is + * dependent on context. For example, a Hedera file is created with a list of keys, where all of + * them must sign a transaction to create or modify the file, but only one of them is needed to sign + * a transaction to delete the file. So it's a single list that sometimes acts as a 1-of-M threshold + * key, and sometimes acts as an M-of-M threshold key. A key list is always an M-of-M, unless + * specified otherwise in documentation. A key list can have nested key lists or threshold keys. + * Nested key lists are always M-of-M. A key list can have repeated primitive public keys, but all + * repeated keys are only required to sign once. + * + * A Key can contain a ThresholdKey or KeyList, which in turn contain a Key, so this mutual + * recursion would allow nesting arbitrarily deep. A ThresholdKey which contains a list of primitive + * keys has 3 levels: ThresholdKey -> KeyList -> Key. A KeyList which contains several primitive + * keys has 2 levels: KeyList -> Key. A Key with 2 levels of nested ThresholdKeys has 7 levels: + * Key -> ThresholdKey -> KeyList -> Key -> ThresholdKey -> KeyList -> Key. + * + * Each Key should not have more than 46 levels, which implies 15 levels of nested ThresholdKeys. + */ +message Key { + oneof key { + /** + * smart contract instance that is authorized as if it had signed with a key + */ + ContractID contractID = 1; + + /** + * Ed25519 public key bytes + */ + bytes ed25519 = 2; + + /** + * (NOT SUPPORTED) RSA-3072 public key bytes + */ + bytes RSA_3072 = 3; + + /** + * (NOT SUPPORTED) ECDSA with the p-384 curve public key bytes + */ + bytes ECDSA_384 = 4; + + /** + * a threshold N followed by a list of M keys, any N of which are required to form a valid + * signature + */ + ThresholdKey thresholdKey = 5; + + /** + * A list of Keys of the Key type. + */ + KeyList keyList = 6; + + /** + * Compressed ECDSA(secp256k1) public key bytes + */ + bytes ECDSA_secp256k1 = 7; + + /** + * A smart contract that, if the recipient of the active message frame, should be treated + * as having signed. (Note this does not mean the code being executed in the frame + * will belong to the given contract, since it could be running another contract's code via + * delegatecall. So setting this key is a more permissive version of setting the + * contractID key, which also requires the code in the active message frame belong to the + * the contract with the given id.) + */ + ContractID delegatable_contract_id = 8; + } +} + +/** + * A set of public keys that are used together to form a threshold signature. If the threshold is N + * and there are M keys, then this is an N of M threshold signature. If an account is associated + * with ThresholdKeys, then a transaction to move cryptocurrency out of it must be signed by a list + * of M signatures, where at most M-N of them are blank, and the other at least N of them are valid + * signatures corresponding to at least N of the public keys listed here. + */ +message ThresholdKey { + /** + * A valid signature set must have at least this many signatures + */ + uint32 threshold = 1; + + /** + * List of all the keys that can sign + */ + KeyList keys = 2; +} + +/** + * A list of keys that requires all keys (M-of-M) to sign unless otherwise specified in + * documentation. A KeyList may contain repeated keys, but all repeated keys are only required to + * sign once. + */ +message KeyList { + /** + * list of keys + */ + repeated Key keys = 1; +} + +/** + * This message is DEPRECATED and UNUSABLE with network nodes. It is retained + * here only for historical reasons. + * + * Please use the SignaturePair and SignatureMap messages. + */ +message Signature { + option deprecated = true; + + oneof signature { + /** + * smart contract virtual signature (always length zero) + */ + bytes contract = 1; + + /** + * ed25519 signature bytes + */ + bytes ed25519 = 2; + + /** + * RSA-3072 signature bytes + */ + bytes RSA_3072 = 3; + + /** + * ECDSA p-384 signature bytes + */ + bytes ECDSA_384 = 4; + + /** + * A list of signatures for a single N-of-M threshold Key. This must be a list of exactly M + * signatures, at least N of which are non-null. + */ + ThresholdSignature thresholdSignature = 5; + + /** + * A list of M signatures, each corresponding to a Key in a KeyList of the same length. + */ + SignatureList signatureList = 6; + } +} + +/** + * This message is DEPRECATED and UNUSABLE with network nodes. It is retained + * here only for historical reasons. + * + * Please use the SignaturePair and SignatureMap messages. + */ +message ThresholdSignature { + option deprecated = true; + + /** + * for an N-of-M threshold key, this is a list of M signatures, at least N of which must be + * non-null + */ + SignatureList sigs = 2; +} + +/** + * This message is DEPRECATED and UNUSABLE with network nodes. It is retained + * here only for historical reasons. + * + * Please use the SignaturePair and SignatureMap messages. + */ +message SignatureList { + option deprecated = true; + + /** + * each signature corresponds to a Key in the KeyList + */ + repeated Signature sigs = 2; +} + +/** + * The client may use any number of bytes from zero to the whole length of the public key for + * pubKeyPrefix. If zero bytes are used, then it must be that only one primitive key is required + * to sign the linked transaction; it will surely resolve to INVALID_SIGNATURE otherwise. + * + * IMPORTANT: In the special case that a signature is being provided for a key used to + * authorize a precompiled contract, the pubKeyPrefix must contain the entire public + * key! That is, if the key is a Ed25519 key, the pubKeyPrefix should be 32 bytes + * long. If the key is a ECDSA(secp256k1) key, the pubKeyPrefix should be 33 bytes long, + * since we require the compressed form of the public key. + * + * Only Ed25519 and ECDSA(secp256k1) keys and hence signatures are currently supported. + */ +message SignaturePair { + /** + * First few bytes of the public key + */ + bytes pubKeyPrefix = 1; + + oneof signature { + /** + * smart contract virtual signature (always length zero) + */ + bytes contract = 2; + + /** + * ed25519 signature + */ + bytes ed25519 = 3; + + /** + * RSA-3072 signature + */ + bytes RSA_3072 = 4; + + /** + * ECDSA p-384 signature + */ + bytes ECDSA_384 = 5; + + /** + * ECDSA(secp256k1) signature + */ + bytes ECDSA_secp256k1 = 6; + } +} + +/** + * A set of signatures corresponding to every unique public key used to sign a given transaction. If + * one public key matches more than one prefixes on the signature map, the transaction containing + * the map will fail immediately with the response code KEY_PREFIX_MISMATCH. + */ +message SignatureMap { + /** + * Each signature pair corresponds to a unique Key required to sign the transaction. + */ + repeated SignaturePair sigPair = 1; +} + +/** + * The transactions and queries supported by Hedera Hashgraph. + */ +enum HederaFunctionality { + /** + * UNSPECIFIED - Need to keep first value as unspecified because first element is ignored and + * not parsed (0 is ignored by parser) + */ + NONE = 0; + + /** + * crypto transfer + */ + CryptoTransfer = 1; + + /** + * crypto update account + */ + CryptoUpdate = 2; + + /** + * crypto delete account + */ + CryptoDelete = 3; + + /** + * Add a livehash to a crypto account + */ + CryptoAddLiveHash = 4; + + /** + * Delete a livehash from a crypto account + */ + CryptoDeleteLiveHash = 5; + + /** + * Smart Contract Call + */ + ContractCall = 6; + + /** + * Smart Contract Create Contract + */ + ContractCreate = 7; + + /** + * Smart Contract update contract + */ + ContractUpdate = 8; + + /** + * File Operation create file + */ + FileCreate = 9; + + /** + * File Operation append file + */ + FileAppend = 10; + + /** + * File Operation update file + */ + FileUpdate = 11; + + /** + * File Operation delete file + */ + FileDelete = 12; + + /** + * crypto get account balance + */ + CryptoGetAccountBalance = 13; + + /** + * crypto get account record + */ + CryptoGetAccountRecords = 14; + + /** + * Crypto get info + */ + CryptoGetInfo = 15; + + /** + * Smart Contract Call + */ + ContractCallLocal = 16; + + /** + * Smart Contract get info + */ + ContractGetInfo = 17; + + /** + * Smart Contract, get the runtime code + */ + ContractGetBytecode = 18; + + /** + * Smart Contract, get by solidity ID + */ + GetBySolidityID = 19; + + /** + * Smart Contract, get by key + */ + GetByKey = 20; + + /** + * Get a live hash from a crypto account + */ + CryptoGetLiveHash = 21; + + /** + * Crypto, get the stakers for the node + */ + CryptoGetStakers = 22; + + /** + * File Operations get file contents + */ + FileGetContents = 23; + + /** + * File Operations get the info of the file + */ + FileGetInfo = 24; + + /** + * Crypto get the transaction records + */ + TransactionGetRecord = 25; + + /** + * Contract get the transaction records + */ + ContractGetRecords = 26; + + /** + * crypto create account + */ + CryptoCreate = 27; + + /** + * system delete file + */ + SystemDelete = 28; + + /** + * system undelete file + */ + SystemUndelete = 29; + + /** + * delete contract + */ + ContractDelete = 30; + + /** + * freeze + */ + Freeze = 31; + + /** + * Create Tx Record + */ + CreateTransactionRecord = 32; + + /** + * Crypto Auto Renew + */ + CryptoAccountAutoRenew = 33; + + /** + * Contract Auto Renew + */ + ContractAutoRenew = 34; + + /** + * Get Version + */ + GetVersionInfo = 35; + + /** + * Transaction Get Receipt + */ + TransactionGetReceipt = 36; + + /** + * Create Topic + */ + ConsensusCreateTopic = 50; + + /** + * Update Topic + */ + ConsensusUpdateTopic = 51; + + /** + * Delete Topic + */ + ConsensusDeleteTopic = 52; + + /** + * Get Topic information + */ + ConsensusGetTopicInfo = 53; + + /** + * Submit message to topic + */ + ConsensusSubmitMessage = 54; + + UncheckedSubmit = 55; + /** + * Create Token + */ + TokenCreate = 56; + + /** + * Get Token information + */ + TokenGetInfo = 58; + + /** + * Freeze Account + */ + TokenFreezeAccount = 59; + + /** + * Unfreeze Account + */ + TokenUnfreezeAccount = 60; + + /** + * Grant KYC to Account + */ + TokenGrantKycToAccount = 61; + + /** + * Revoke KYC from Account + */ + TokenRevokeKycFromAccount = 62; + + /** + * Delete Token + */ + TokenDelete = 63; + + /** + * Update Token + */ + TokenUpdate = 64; + + /** + * Mint tokens to treasury + */ + TokenMint = 65; + + /** + * Burn tokens from treasury + */ + TokenBurn = 66; + + /** + * Wipe token amount from Account holder + */ + TokenAccountWipe = 67; + + /** + * Associate tokens to an account + */ + TokenAssociateToAccount = 68; + + /** + * Dissociate tokens from an account + */ + TokenDissociateFromAccount = 69; + + /** + * Create Scheduled Transaction + */ + ScheduleCreate = 70; + + /** + * Delete Scheduled Transaction + */ + ScheduleDelete = 71; + + /** + * Sign Scheduled Transaction + */ + ScheduleSign = 72; + + /** + * Get Scheduled Transaction Information + */ + ScheduleGetInfo = 73; + + /** + * Get Token Account Nft Information + */ + TokenGetAccountNftInfos = 74; + + /** + * Get Token Nft Information + */ + TokenGetNftInfo = 75; + + /** + * Get Token Nft List Information + */ + TokenGetNftInfos = 76; + + /** + * Update a token's custom fee schedule, if permissible + */ + TokenFeeScheduleUpdate = 77; + + /** + * Get execution time(s) by TransactionID, if available + */ + NetworkGetExecutionTime = 78; + + /** + * Pause the Token + */ + TokenPause = 79; + + /** + * Unpause the Token + */ + TokenUnpause = 80; + + /** + * Approve allowance for a spender relative to the owner account + */ + CryptoApproveAllowance = 81; + + /** + * Deletes granted allowances on owner account + */ + CryptoDeleteAllowance = 82; + + /** + * Gets all the information about an account, including balance and allowances. This does not get the list of + * account records. + */ + GetAccountDetails = 83; + + /** + * Ethereum Transaction + */ + EthereumTransaction = 84; + + /** + * Updates the staking info at the end of staking period to indicate new staking period has started. + */ + NodeStakeUpdate = 85; + + /** + * Generates a pseudorandom number. + */ + UtilPrng = 86; +} + +/** + * A set of prices the nodes use in determining transaction and query fees, and constants involved + * in fee calculations. Nodes multiply the amount of resources consumed by a transaction or query + * by the corresponding price to calculate the appropriate fee. Units are one-thousandth of a + * tinyCent. + */ +message FeeComponents { + /** + * A minimum, the calculated fee must be greater than this value + */ + int64 min = 1; + + /** + * A maximum, the calculated fee must be less than this value + */ + int64 max = 2; + + /** + * A constant contribution to the fee + */ + int64 constant = 3; + + /** + * The price of bandwidth consumed by a transaction, measured in bytes + */ + int64 bpt = 4; + + /** + * The price per signature verification for a transaction + */ + int64 vpt = 5; + + /** + * The price of RAM consumed by a transaction, measured in byte-hours + */ + int64 rbh = 6; + + /** + * The price of storage consumed by a transaction, measured in byte-hours + */ + int64 sbh = 7; + + /** + * The price of computation for a smart contract transaction, measured in gas + */ + int64 gas = 8; + + /** + * The price per hbar transferred for a transfer + */ + int64 tv = 9; + + /** + * The price of bandwidth for data retrieved from memory for a response, measured in bytes + */ + int64 bpr = 10; + + /** + * The price of bandwidth for data retrieved from disk for a response, measured in bytes + */ + int64 sbpr = 11; +} + +/** + * The fees for a specific transaction or query based on the fee data. + */ +message TransactionFeeSchedule { + /** + * A particular transaction or query + */ + HederaFunctionality hederaFunctionality = 1; + + /** + * Resource price coefficients + */ + FeeData feeData = 2 [deprecated=true]; + + /** + * Resource price coefficients. Supports subtype price definition. + */ + repeated FeeData fees = 3; +} + +/** + * The total fee charged for a transaction. It is composed of three components – a node fee that + * compensates the specific node that submitted the transaction, a network fee that compensates the + * network for assigning the transaction a consensus timestamp, and a service fee that compensates + * the network for the ongoing maintenance of the consequences of the transaction. + */ +message FeeData { + /** + * Fee paid to the submitting node + */ + FeeComponents nodedata = 1; + + /** + * Fee paid to the network for processing a transaction into consensus + */ + FeeComponents networkdata = 2; + + /** + * Fee paid to the network for providing the service associated with the + * transaction; for instance, storing a file + */ + FeeComponents servicedata = 3; + + /** + * SubType distinguishing between different types of FeeData, correlating + * to the same HederaFunctionality + */ + SubType subType = 4; +} + +/** + * A list of resource prices fee for different transactions and queries and the time period at which + * this fee schedule will expire. Nodes use the prices to determine the fees for all transactions + * based on how much of those resources each transaction uses. + */ +message FeeSchedule { + /** + * List of price coefficients for network resources + */ + repeated TransactionFeeSchedule transactionFeeSchedule = 1; + + /** + * FeeSchedule expiry time + */ + TimestampSeconds expiryTime = 2; +} + +/** + * This contains two Fee Schedules with expiry timestamp. + */ +message CurrentAndNextFeeSchedule { + /** + * Contains current Fee Schedule + */ + FeeSchedule currentFeeSchedule = 1; + + /** + * Contains next Fee Schedule + */ + FeeSchedule nextFeeSchedule = 2; +} + +/** + * Contains the IP address and the port representing a service endpoint of a Node in a network. Used + * to reach the Hedera API and submit transactions to the network. + */ +message ServiceEndpoint { + /** + * The 32-bit IPv4 address of the node encoded in left to right order (e.g. 127.0.0.1 has 127 + * as its first byte) + */ + bytes ipAddressV4 = 1; + + /** + * The port of the node + */ + int32 port = 2; +} + +/** + * The data about a node, including its service endpoints and the Hedera account to be paid for + * services provided by the node (that is, queries answered and transactions submitted.) + * + * If the serviceEndpoint list is not set, or empty, then the endpoint given by the + * (deprecated) ipAddress and portno fields should be used. + * + * All fields are populated in the 0.0.102 address book file while only fields that start with # are + * populated in the 0.0.101 address book file. + */ +message NodeAddress { + /** + * The IP address of the Node with separator & octets encoded in UTF-8. Usage is deprecated, + * ServiceEndpoint is preferred to retrieve a node's list of IP addresses and ports + */ + bytes ipAddress = 1 [deprecated=true]; + + /** + * The port number of the grpc server for the node. Usage is deprecated, ServiceEndpoint is + * preferred to retrieve a node's list of IP addresses and ports + */ + int32 portno = 2 [deprecated=true]; + + /** + * Usage is deprecated, nodeAccountId is preferred to retrieve a node's account ID + */ + bytes memo = 3 [deprecated=true]; + + /** + * The node's X509 RSA public key used to sign stream files (e.g., record stream + * files). Precisely, this field is a string of hexadecimal characters which, + * translated to binary, are the public key's DER encoding. + */ + string RSA_PubKey = 4; + + /** + * # A non-sequential identifier for the node + */ + int64 nodeId = 5; + + /** + * # The account to be paid for queries and transactions sent to this node + */ + AccountID nodeAccountId = 6; + + /** + * # Hash of the node's TLS certificate. Precisely, this field is a string of + * hexadecimal characters which, translated to binary, are the SHA-384 hash of + * the UTF-8 NFKD encoding of the node's TLS cert in PEM format. Its value can be + * used to verify the node's certificate it presents during TLS negotiations. + */ + bytes nodeCertHash = 7; + + /** + * # A node's service IP addresses and ports + */ + repeated ServiceEndpoint serviceEndpoint = 8; + + /** + * A description of the node, with UTF-8 encoding up to 100 bytes + */ + string description = 9; + + /** + * [Deprecated] The amount of tinybars staked to the node + */ + int64 stake = 10 [deprecated = true]; +} + +/** + * A list of nodes and their metadata that contains all details of the nodes for the network. Used + * to parse the contents of system files 0.0.101 and 0.0.102. + */ +message NodeAddressBook { + /** + * Metadata of all nodes in the network + */ + repeated NodeAddress nodeAddress = 1; +} + +/** + * Hedera follows semantic versioning (https://semver.org/) for both the HAPI protobufs and the + * Services software. This type allows the getVersionInfo query in the + * NetworkService to return the deployed versions of both protobufs and software on the + * node answering the query. + */ +message SemanticVersion { + /** + * Increases with incompatible API changes + */ + int32 major = 1; + + /** + * Increases with backwards-compatible new functionality + */ + int32 minor = 2; + + /** + * Increases with backwards-compatible bug fixes + */ + int32 patch = 3; + + /** + * A pre-release version MAY be denoted by appending a hyphen and a series of dot separated + * identifiers (https://semver.org/#spec-item-9); so given a semver 0.14.0-alpha.1+21AF26D3, + * this field would contain 'alpha.1' + */ + string pre = 4; + + /** + * Build metadata MAY be denoted by appending a plus sign and a series of dot separated + * identifiers immediately following the patch or pre-release version + * (https://semver.org/#spec-item-10); so given a semver 0.14.0-alpha.1+21AF26D3, this field + * would contain '21AF26D3' + */ + string build = 5; +} + +/** + * UNDOCUMENTED + */ +message Setting { + /** + * name of the property + */ + string name = 1; + + /** + * value of the property + */ + string value = 2; + + /** + * any data associated with property + */ + bytes data = 3; +} + +/** + * UNDOCUMENTED + */ +message ServicesConfigurationList { + /** + * list of name value pairs of the application properties + */ + repeated Setting nameValue = 1; +} + +/** + * Token's information related to the given Account + */ +message TokenRelationship { + /** + * The ID of the token + */ + TokenID tokenId = 1; + + /** + * The Symbol of the token + */ + string symbol = 2; + + /** + * For token of type FUNGIBLE_COMMON - the balance that the Account holds in the smallest + * denomination. For token of type NON_FUNGIBLE_UNIQUE - the number of NFTs held by the account + */ + uint64 balance = 3; + + /** + * The KYC status of the account (KycNotApplicable, Granted or Revoked). If the token does not + * have KYC key, KycNotApplicable is returned + */ + TokenKycStatus kycStatus = 4; + + /** + * The Freeze status of the account (FreezeNotApplicable, Frozen or Unfrozen). If the token does + * not have Freeze key, FreezeNotApplicable is returned + */ + TokenFreezeStatus freezeStatus = 5; + + /** + * Tokens divide into 10decimals pieces + */ + uint32 decimals = 6; + + /** + * Specifies if the relationship is created implicitly. False : explicitly associated, True : + * implicitly associated. + */ + bool automatic_association = 7; +} + +/** + * A number of transferable units of a certain token. + * + * The transferable unit of a token is its smallest denomination, as given by the token's + * decimals property---each minted token contains 10decimals + * transferable units. For example, we could think of the cent as the transferable unit of the US + * dollar (decimals=2); and the tinybar as the transferable unit of hbar + * (decimals=8). + * + * Transferable units are not directly comparable across different tokens. + */ +message TokenBalance { + /** + * A unique token id + */ + TokenID tokenId = 1; + + /** + * Number of transferable units of the identified token. For token of type FUNGIBLE_COMMON - + * balance in the smallest denomination. For token of type NON_FUNGIBLE_UNIQUE - the number of + * NFTs held by the account + */ + uint64 balance = 2; + + /** + * Tokens divide into 10decimals pieces + */ + uint32 decimals = 3; +} + +/** + * A sequence of token balances + */ +message TokenBalances { + repeated TokenBalance tokenBalances = 1; +} + +/* A token - account association */ +message TokenAssociation { + TokenID token_id = 1; // The token involved in the association + AccountID account_id = 2; // The account involved in the association +} + +/** + * Staking metadata for an account or a contract returned in CryptoGetInfo or ContractGetInfo queries + */ +message StakingInfo { + + /** + * If true, this account or contract declined to receive a staking reward. + */ + bool decline_reward = 1; + + /** + * The staking period during which either the staking settings for this account or contract changed (such as starting + * staking or changing staked_node_id) or the most recent reward was earned, whichever is later. If this account or contract + * is not currently staked to a node, then this field is not set. + */ + Timestamp stake_period_start = 2; + + /** + * The amount in tinybars that will be received in the next reward situation. + */ + int64 pending_reward = 3; + + /** + * The total of balance of all accounts staked to this account or contract. + */ + int64 staked_to_me = 4; + + /** + * ID of the account or node to which this account or contract is staking. + */ + oneof staked_id { + + /** + * The account to which this account or contract is staking. + */ + AccountID staked_account_id = 5; + + /** + * The ID of the node this account or contract is staked to. + */ + int64 staked_node_id = 6; + } +} diff --git a/src/Hedera/Protobuf/crypto_transfer.proto b/src/Hedera/Protobuf/crypto_transfer.proto new file mode 100644 index 00000000000..0c6c0b1b23e --- /dev/null +++ b/src/Hedera/Protobuf/crypto_transfer.proto @@ -0,0 +1,55 @@ +// Taken from https://github.com/hashgraph/hedera-protobufs/tree/main/services + +syntax = "proto3"; + +package proto; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +option java_package = "com.hederahashgraph.api.proto.java"; +option java_multiple_files = true; + +import "basic_types.proto"; + +/** + * Transfers cryptocurrency among two or more accounts by making the desired adjustments to their + * balances. Each transfer list can specify up to 10 adjustments. Each negative amount is withdrawn + * from the corresponding account (a sender), and each positive one is added to the corresponding + * account (a receiver). The amounts list must sum to zero. Each amount is a number of tinybars + * (there are 100,000,000 tinybars in one hbar). If any sender account fails to have sufficient + * hbars, then the entire transaction fails, and none of those transfers occur, though the + * transaction fee is still charged. This transaction must be signed by the keys for all the sending + * accounts, and for any receiving accounts that have receiverSigRequired == true. The signatures + * are in the same order as the accounts, skipping those accounts that don't need a signature. + */ +message CryptoTransferTransactionBody { + /** + * The desired hbar balance adjustments + */ + TransferList transfers = 1; + + /** + * The desired token unit balance adjustments; if any custom fees are assessed, the ledger will + * try to deduct them from the payer of this CryptoTransfer, resolving the transaction to + * INSUFFICIENT_PAYER_BALANCE_FOR_CUSTOM_FEE if this is not possible + */ + repeated TokenTransferList tokenTransfers = 2; +} diff --git a/src/Hedera/Protobuf/duration.proto b/src/Hedera/Protobuf/duration.proto new file mode 100644 index 00000000000..2b096068c8c --- /dev/null +++ b/src/Hedera/Protobuf/duration.proto @@ -0,0 +1,38 @@ +// Taken from https://github.com/hashgraph/hedera-protobufs/tree/main/services + +syntax = "proto3"; + +package proto; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +option java_package = "com.hederahashgraph.api.proto.java"; +option java_multiple_files = true; + +/** + * A length of time in seconds. + */ +message Duration { + /** + * The number of seconds + */ + int64 seconds = 1; +} diff --git a/src/Hedera/Protobuf/timestamp.proto b/src/Hedera/Protobuf/timestamp.proto new file mode 100644 index 00000000000..b7e8e9a8831 --- /dev/null +++ b/src/Hedera/Protobuf/timestamp.proto @@ -0,0 +1,54 @@ +// Taken from https://github.com/hashgraph/hedera-protobufs/tree/main/services + +syntax = "proto3"; + +package proto; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +option java_package = "com.hederahashgraph.api.proto.java"; +option java_multiple_files = true; + +/** + * An exact date and time. This is the same data structure as the protobuf Timestamp.proto (see the + * comments in https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto) + */ +message Timestamp { + /** + * Number of complete seconds since the start of the epoch + */ + int64 seconds = 1; + + /** + * Number of nanoseconds since the start of the last second + */ + int32 nanos = 2; +} + +/** + * An exact date and time, with a resolution of one second (no nanoseconds). + */ +message TimestampSeconds { + /** + * Number of complete seconds since the start of the epoch + */ + int64 seconds = 1; +} diff --git a/src/Hedera/Protobuf/transaction_body.proto b/src/Hedera/Protobuf/transaction_body.proto new file mode 100644 index 00000000000..0687b6f851d --- /dev/null +++ b/src/Hedera/Protobuf/transaction_body.proto @@ -0,0 +1,76 @@ +// Taken from https://github.com/hashgraph/hedera-protobufs/tree/main/services + +syntax = "proto3"; + +package proto; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +option java_package = "com.hederahashgraph.api.proto.java"; +option java_multiple_files = true; + +import "crypto_transfer.proto"; +import "duration.proto"; +import "basic_types.proto"; + +/** + * A single transaction. All transaction types are possible here. + */ +message TransactionBody { + /** + * The ID for this transaction, which includes the payer's account (the account paying the + * transaction fee). If two transactions have the same transactionID, they won't both have an + * effect + */ + TransactionID transactionID = 1; + + /** + * The account of the node that submits the client's transaction to the network + */ + AccountID nodeAccountID = 2; + + /** + * The maximum transaction fee the client is willing to pay + */ + uint64 transactionFee = 3; + + /** + * The transaction is invalid if consensusTimestamp > transactionID.transactionValidStart + + * transactionValidDuration + */ + Duration transactionValidDuration = 4; + + /** + * Any notes or descriptions that should be put into the record (max length 100) + */ + string memo = 6; + + /** + * The choices here are arranged by service in roughly lexicographical order. The field ordinals are non-sequential, and a result of the historical order of implementation. + */ + oneof data { + + /** + * Transfer amount between accounts + */ + CryptoTransferTransactionBody cryptoTransfer = 14; + } +} diff --git a/src/Hedera/Protobuf/transaction_contents.proto b/src/Hedera/Protobuf/transaction_contents.proto new file mode 100644 index 00000000000..2af320a2220 --- /dev/null +++ b/src/Hedera/Protobuf/transaction_contents.proto @@ -0,0 +1,42 @@ +// Taken from https://github.com/hashgraph/hedera-protobufs/tree/main/services + +syntax = "proto3"; + +package proto; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +option java_package = "com.hederahashgraph.api.proto.java"; +option java_multiple_files = true; + +import "basic_types.proto"; + +message SignedTransaction { + /** + * TransactionBody serialized into bytes, which needs to be signed + */ + bytes bodyBytes = 1; + + /** + * The signatures on the body with the new format, to authorize the transaction + */ + SignatureMap sigMap = 2; +} diff --git a/src/Hedera/Signer.cpp b/src/Hedera/Signer.cpp new file mode 100644 index 00000000000..feb90281317 --- /dev/null +++ b/src/Hedera/Signer.cpp @@ -0,0 +1,107 @@ +// Copyright © 2017-2022 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 "Address.h" +#include "HexCoding.h" +#include "Protobuf/transaction_body.pb.h" +#include "Protobuf/transaction_contents.pb.h" +#include "../PublicKey.h" + +namespace TW::Hedera::internals { +static inline proto::AccountID accountIDfromStr(const std::string& input) { + const auto hederaAccount = Address(input); + auto accountID = proto::AccountID(); + accountID.set_accountnum(static_cast(hederaAccount.num())); + accountID.set_realmnum(static_cast(hederaAccount.realm())); + accountID.set_shardnum(static_cast(hederaAccount.shard())); + return accountID; +} + +static inline proto::Timestamp timestampFromTWProto(const Proto::Timestamp& input) { + auto timestamp = proto::Timestamp(); + timestamp.set_seconds(input.seconds()); + timestamp.set_nanos(input.nanos()); + return timestamp; +} + +static inline proto::TransactionID transactionIDFromSigningInput(const Proto::SigningInput& input) { + auto transactionID = proto::TransactionID(); + *transactionID.mutable_transactionvalidstart() = timestampFromTWProto(input.body().transactionid().transactionvalidstart()); + *transactionID.mutable_accountid() = accountIDfromStr(input.body().transactionid().accountid()); + return transactionID; +} + +static inline proto::TransactionBody transactionBodyPrerequisites(const Proto::SigningInput& input) { + auto body = proto::TransactionBody(); + body.set_memo(input.body().memo()); + body.set_transactionfee(input.body().transactionfee()); + *body.mutable_nodeaccountid() = accountIDfromStr(input.body().nodeaccountid()); + body.mutable_transactionvalidduration()->set_seconds(input.body().transactionvalidduration()); + *body.mutable_transactionid() = transactionIDFromSigningInput(input); + return body; +} + +static inline proto::TransferList transferListFromInput(const Proto::SigningInput& input) { + auto transferList = proto::TransferList(); + auto fromAccountID = accountIDfromStr(input.body().transfer().from()); + auto amount = input.body().transfer().amount(); + auto* to = transferList.add_accountamounts(); + to->set_amount(amount); + *to->mutable_accountid() = accountIDfromStr(input.body().transfer().to()); + auto* from = transferList.add_accountamounts(); + from->set_amount(-amount); + *from->mutable_accountid() = fromAccountID; + return transferList; +} + +static inline proto::CryptoTransferTransactionBody cryptoTransferFromInput(const Proto::SigningInput& input) { + auto transferList = transferListFromInput(input); + auto cryptoTransfer = proto::CryptoTransferTransactionBody(); + *cryptoTransfer.mutable_transfers() = transferList; + return cryptoTransfer; +} + +static inline Proto::SigningOutput sign(const proto::TransactionBody& body, const PrivateKey& privateKey) { + auto protoOutput = Proto::SigningOutput(); + Data encoded; + auto encodedBody = data(body.SerializeAsString()); + auto signedBody = privateKey.sign(encodedBody, TWCurveED25519); + auto sigMap = proto::SignatureMap(); + auto* sigPair = sigMap.add_sigpair(); + sigPair->set_ed25519(signedBody.data(), signedBody.size()); + auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes; + sigPair->set_pubkeyprefix(pubKey.data(), pubKey.size()); + auto signedTx = proto::SignedTransaction(); + signedTx.set_bodybytes(encodedBody.data(), encodedBody.size()); + *signedTx.mutable_sigmap() = sigMap; + encoded = data(signedTx.SerializeAsString()); + protoOutput.set_encoded(encoded.data(), encoded.size()); + return protoOutput; +} + +} // namespace TW::Hedera::internals + +namespace TW::Hedera { + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { + auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); + auto body = internals::transactionBodyPrerequisites(input); + + switch (input.body().data_case()) { + case Proto::TransactionBody::kTransfer: { + *body.mutable_cryptotransfer() = internals::cryptoTransferFromInput(input); + break; + } + case Proto::TransactionBody::DATA_NOT_SET: + break; + default: + break; + } + return internals::sign(body, privateKey); +} + +} // namespace TW::Hedera diff --git a/src/Hedera/Signer.h b/src/Hedera/Signer.h new file mode 100644 index 00000000000..19a54e3052a --- /dev/null +++ b/src/Hedera/Signer.h @@ -0,0 +1,25 @@ +// Copyright © 2017-2022 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 "../PrivateKey.h" +#include "../proto/Hedera.pb.h" + +namespace TW::Hedera { + +/// Helper class that performs Hedera transaction signing. +class Signer { +public: + /// Hide default constructor + Signer() = delete; + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; +}; + +} // namespace TW::Hedera diff --git a/src/algorithm/string.hpp b/src/algorithm/string.hpp new file mode 100644 index 00000000000..c5c1bfdfe79 --- /dev/null +++ b/src/algorithm/string.hpp @@ -0,0 +1,27 @@ +// Copyright © 2017-2022 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 +#include +#include + +namespace TW { + +static std::vector ssplit(const std::string& input, char delimiter) { + std::istringstream iss(input); + std::vector tokens; + std::string token; + while (std::getline(iss, token, delimiter)) { + if (!token.empty()) { + tokens.emplace_back(token); + } + } + return tokens; +} + +} // namespace TW diff --git a/src/proto/Hedera.proto b/src/proto/Hedera.proto new file mode 100644 index 00000000000..ff9a2746eaf --- /dev/null +++ b/src/proto/Hedera.proto @@ -0,0 +1,98 @@ +// Copyright © 2017-2022 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. + +syntax = "proto3"; + +package TW.Hedera.Proto; +option java_package = "wallet.core.jni.proto"; + +// An exact date and time. This is the same data structure as the protobuf Timestamp.proto +// (see the comments in https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto) +message Timestamp { + // Number of complete seconds since the start of the epoch + int64 seconds = 1; + // Number of nanoseconds since the start of the last second + int32 nanos = 2; +} + +// The ID for a transaction. This is used for retrieving receipts and records for a transaction, for +// appending to a file right after creating it, for instantiating a smart contract with bytecode in +// a file just created, and internally by the network for detecting when duplicate transactions are +// submitted. A user might get a transaction processed faster by submitting it to N nodes, each with +// a different node account, but all with the same TransactionID. Then, the transaction will take +// effect when the first of all those nodes submits the transaction and it reaches consensus. The +// other transactions will not take effect. So this could make the transaction take effect faster, +// if any given node might be slow. However, the full transaction fee is charged for each +// transaction, so the total fee is N times as much if the transaction is sent to N nodes. +// +// Applicable to Scheduled Transactions: +// - The ID of a Scheduled Transaction has transactionValidStart and accountIDs inherited from the +// ScheduleCreate transaction that created it. That is to say that they are equal +// - The scheduled property is true for Scheduled Transactions +// - transactionValidStart, accountID and scheduled properties should be omitted +message TransactionID { + // The transaction is invalid if consensusTimestamp < transactionID.transactionStartValid + Timestamp transactionValidStart = 1; + // The Account ID that paid for this transaction + string accountID = 2; + // Whether the Transaction is of type Scheduled or no + bool scheduled = 3; + // The identifier for an internal transaction that was spawned as part + // of handling a user transaction. (These internal transactions share the + // transactionValidStart and accountID of the user transaction, so a + // nonce is necessary to give them a unique TransactionID.) + // + // An example is when a "parent" ContractCreate or ContractCall transaction + // calls one or more HTS precompiled contracts; each of the "child" transactions spawned for a precompile has a id + // with a different nonce. + int32 nonce = 4; +} + +// Necessary fields to process a TransferMessage +message TransferMessage { + // Source Account address (string) + string from = 1; + // Destination Account address (string) + string to = 2; + // Amount to be transferred (sint64) + sint64 amount = 3; +} + +// A single transaction. All transaction types are possible here. +message TransactionBody { + // The ID for this transaction, which includes the payer's account (the account paying the transaction fee). + // If two transactions have the same transactionID, they won't both have an effect + TransactionID transactionID = 1; + // The account of the node that submits the client's transaction to the network + string nodeAccountID = 2; + // The maximum transaction fee the client is willing to pay + uint64 transactionFee = 3; + // The transaction is invalid if consensusTimestamp > transactionID.transactionValidStart + transactionValidDuration + int64 transactionValidDuration = 4; + // Any notes or descriptions that should be put into the record (max length 100) + string memo = 5; + // The choices here are arranged by service in roughly lexicographical order. The field ordinals are non-sequential, + // and a result of the historical order of implementation. + oneof data { + // Transfer amount between accounts + TransferMessage transfer = 6; + } +} + +// Input data necessary to create a signed transaction. +message SigningInput { + // Private key to sign the transaction (bytes) + bytes private_key = 1; + + // The transaction body + TransactionBody body = 2; +} + +// Transaction signing output. +message SigningOutput { + // Signed and encoded transaction bytes. + bytes encoded = 1; +} diff --git a/swift/Tests/Blockchains/HederaTests.swift b/swift/Tests/Blockchains/HederaTests.swift new file mode 100644 index 00000000000..c71dd7d1f87 --- /dev/null +++ b/swift/Tests/Blockchains/HederaTests.swift @@ -0,0 +1,59 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class HederaTests: XCTestCase { + func testAddress() { + let anyAddress = AnyAddress(string: "0.0.1377988", coin: .hedera) + + XCTAssertEqual(anyAddress?.description, "0.0.1377988") + XCTAssertEqual(anyAddress?.coin, .hedera) + + let invalid = "0.0.a" + XCTAssertNil(Data(hexString: invalid)) + XCTAssertNil(AnyAddress(string: invalid, coin: .hedera)) + XCTAssertFalse(AnyAddress.isValid(string: invalid, coin: .hedera)) + } + + func testSignSimpleTransfer() { + // Successfully broadcasted: https://hashscan.io/testnet/transaction/0.0.48694347-1667222879-749068449?t=1667222891.440398729&p=1 + let privateKeyData = Data(hexString: "e87a5584c0173263e138db689fdb2a7389025aaae7cb1a18a1017d76012130e8")! + + let transfer = HederaTransferMessage.with { + $0.amount = 100000000 + $0.from = "0.0.48694347" + $0.to = "0.0.48462050" + } + + let transactionID = HederaTransactionID.with { + $0.accountID = "0.0.48694347" + $0.transactionValidStart = HederaTimestamp.with { + $0.seconds = 1667222879 + $0.nanos = 749068449 + } + } + + let body = HederaTransactionBody.with { + $0.memo = "" + $0.nodeAccountID = "0.0.9" + $0.transactionFee = 100000000 + $0.transactionValidDuration = 120 + $0.transactionID = transactionID + $0.transfer = transfer + } + + let input = HederaSigningInput.with { + $0.privateKey = privateKeyData + $0.body = body + } + + let output: HederaSigningOutput = AnySigner.sign(input: input, coin: .hedera) + let expectedEncoded = "0a440a150a0c08df9aff9a0610a1c197e502120518cb889c17120218091880c2d72f22020878721e0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f12660a640a205d3a70d08b2beafb72c7a68986b3ff819a306078b8c359d739e4966e82e6d40e1a40612589c3b15f1e3ed6084b5a3a5b1b81751578cac8d6c922f31731b3982a5bac80a22558b2197276f5bae49b62503a4d39448ceddbc5ef3ba9bee4c0f302f70c" + XCTAssertEqual(output.encoded.hexString, expectedEncoded) + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 5dd2b6d4ef4..83311e02ca9 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -256,6 +256,10 @@ class CoinAddressDerivationTests: XCTestCase { case .aptos: let expectedResult = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .hedera: + let expectedResult = "0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5"; + assertCoinDerivation(coin, expectedResult, derivedAddress, address) + @unknown default: fatalError() } diff --git a/tests/chains/Hedera/AddressTests.cpp b/tests/chains/Hedera/AddressTests.cpp new file mode 100644 index 00000000000..9a740750d41 --- /dev/null +++ b/tests/chains/Hedera/AddressTests.cpp @@ -0,0 +1,58 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "Hedera/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" + +#include "TestUtilities.h" + +#include +#include + +namespace TW::Hedera::tests { + +TEST(HederaAddress, FromStandardArgument) { + { + // 0.0.1377988 + Address addr(0uL, 0uL, 1'377'988uL); + ASSERT_EQ(addr.shard(), 0uL); + ASSERT_EQ(addr.realm(), 0uL); + ASSERT_EQ(addr.num(), 1'377'988uL); + ASSERT_EQ(addr.string(), "0.0.1377988"); + ASSERT_TRUE(addr.isValid(addr.string())); + } + + { + // 0.0.302a300506032b65700321007df3e1ab790b28de4706d36a7aa99a0e043cb3e2c3d6ec6686e4af7f638b0860 + // https://github.com/hashgraph/hedera-sdk-rust/blob/c1c10d5750552e6bb857132cc824c430bd890a6b/sdk/rust/src/key/public_key/mod.rs#L306 + auto pubkey = PublicKey(parse_hex("7df3e1ab790b28de4706d36a7aa99a0e043cb3e2c3d6ec6686e4af7f638b0860"), TWPublicKeyTypeED25519); + Address addr(0uL, 0uL, 0uL, pubkey); + ASSERT_EQ(addr.shard(), 0uL); + ASSERT_EQ(addr.realm(), 0uL); + ASSERT_EQ(addr.num(), 0uL); + ASSERT_EQ(addr.alias().string(), "302a300506032b65700321007df3e1ab790b28de4706d36a7aa99a0e043cb3e2c3d6ec6686e4af7f638b0860"); + ASSERT_EQ(addr.string(), "0.0.302a300506032b65700321007df3e1ab790b28de4706d36a7aa99a0e043cb3e2c3d6ec6686e4af7f638b0860"); + ASSERT_TRUE(addr.isValid(addr.string())); + } +} + +TEST(HederaAddress, Valid) { + ASSERT_FALSE(Address::isValid("invalid")); + ASSERT_FALSE(Address::isValid("302a300506032b65700321007df3e1ab790b28de4706d36a7aa99a0e043cb3e2c3d6ec6686e4af7f638b0860")); + ASSERT_FALSE(Address::isValid("0.0.abc")); + ASSERT_TRUE(Address::isValid("0.0.1")); + ASSERT_TRUE(Address::isValid("0.0.1377988")); + ASSERT_TRUE(Address::isValid("0.0.302a300506032b65700321007df3e1ab790b28de4706d36a7aa99a0e043cb3e2c3d6ec6686e4af7f638b0860")); +} + +TEST(HederaAddress, FromString) { + auto address = Address("0.0.1377988"); + ASSERT_EQ(address.string(), "0.0.1377988"); +} + +} // namespace TW::Hedera::tests diff --git a/tests/chains/Hedera/SignerTests.cpp b/tests/chains/Hedera/SignerTests.cpp new file mode 100644 index 00000000000..f39c6d10915 --- /dev/null +++ b/tests/chains/Hedera/SignerTests.cpp @@ -0,0 +1,238 @@ +// Copyright © 2017-2022 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 "Hedera/Address.h" +#include "Hedera/Protobuf/basic_types.pb.h" +#include "Hedera/Protobuf/crypto_transfer.pb.h" +#include "Hedera/Protobuf/transaction_body.pb.h" +#include "Hedera/Signer.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" + +#include + +namespace TW::Hedera::tests { + +TEST(HederaSigner, Sign) { + // Successfully broadcasted: https://hashscan.io/testnet/transaction/0.0.48694347-1667222879-749068449?t=1667222891.440398729&p=1 + Proto::SigningInput input; + auto privateKey = PrivateKey(parse_hex("e87a5584c0173263e138db689fdb2a7389025aaae7cb1a18a1017d76012130e8")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto* body = input.mutable_body(); + + *body->mutable_memo() = ""; + *body->mutable_nodeaccountid() = "0.0.9"; + body->set_transactionfee(100000000); + body->set_transactionvalidduration(120); + auto* transferMsg = body->mutable_transfer(); + transferMsg->set_from("0.0.48694347"); + transferMsg->set_to("0.0.48462050"); + transferMsg->set_amount(100000000); + + auto* transactionID = body->mutable_transactionid(); + transactionID->mutable_transactionvalidstart()->set_seconds(1667222879); + transactionID->mutable_transactionvalidstart()->set_nanos(749068449); + transactionID->set_accountid("0.0.48694347"); + + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.encoded()), "0a440a150a0c08df9aff9a0610a1c197e502120518cb889c17120218091880c2d72f22020878721e0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f12660a640a205d3a70d08b2beafb72c7a68986b3ff819a306078b8c359d739e4966e82e6d40e1a40612589c3b15f1e3ed6084b5a3a5b1b81751578cac8d6c922f31731b3982a5bac80a22558b2197276f5bae49b62503a4d39448ceddbc5ef3ba9bee4c0f302f70c"); +} + +TEST(HederaSigner, SignWithMemo) { + // Successfully broadcasted: https://hashscan.io/testnet/transaction/0.0.48694347-1667227300-854561449?t=1667227312.554926003 + Proto::SigningInput input; + auto privateKey = PrivateKey(parse_hex("e87a5584c0173263e138db689fdb2a7389025aaae7cb1a18a1017d76012130e8")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto* body = input.mutable_body(); + + *body->mutable_memo() = "wallet core"; + *body->mutable_nodeaccountid() = "0.0.7"; + body->set_transactionfee(100000000); + body->set_transactionvalidduration(120); + auto* transferMsg = body->mutable_transfer(); + transferMsg->set_from("0.0.48694347"); + transferMsg->set_to("0.0.48462050"); + transferMsg->set_amount(100000000); + + auto* transactionID = body->mutable_transactionid(); + transactionID->mutable_transactionvalidstart()->set_seconds(1667227300); + transactionID->mutable_transactionvalidstart()->set_nanos(854561449); + transactionID->set_accountid("0.0.48694347"); + + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.encoded()), "0a510a150a0c08a4bdff9a0610a9a5be9703120518cb889c17120218071880c2d72f22020878320b77616c6c657420636f7265721e0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f12660a640a205d3a70d08b2beafb72c7a68986b3ff819a306078b8c359d739e4966e82e6d40e1a40ee1764c9acf79b68a675c1a78c8c43cb7d136f5f230b48b44992ad3e7ba87a8256758b823120a76142e58b94f082a0551000cf68cd3336fc4393c6b2191d8603"); +} + +TEST(HederaSigner, SignWithMemoMainnet) { + // Successfully broadcasted: https://hashscan.io/mainnet/transaction/0.0.1377988-1667566445-926176449?t=1667566457.533804616 + Proto::SigningInput input; + auto privateKey = PrivateKey(parse_hex("650c5120cbdc6244e3d10001eb27eea4dd3f80c331b3b6969fa434797d4edd50")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto* body = input.mutable_body(); + + *body->mutable_memo() = "wallet core"; + *body->mutable_nodeaccountid() = "0.0.12"; + body->set_transactionfee(100000000); + body->set_transactionvalidduration(120); + auto* transferMsg = body->mutable_transfer(); + transferMsg->set_from("0.0.1377988"); + transferMsg->set_to("0.0.19783"); + transferMsg->set_amount(100000000); + + auto* transactionID = body->mutable_transactionid(); + transactionID->mutable_transactionvalidstart()->set_seconds(1667566445); + transactionID->mutable_transactionvalidstart()->set_nanos(926176449); + transactionID->set_accountid("0.0.1377988"); + + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.encoded()), "0a4e0a140a0c08ed96949b0610c1a9d1b903120418c48d541202180c1880c2d72f22020878320b77616c6c657420636f7265721c0a1a0a0b0a0418c79a01108084af5f0a0b0a0418c48d5410ff83af5f12660a640a207df3e1ab790b28de4706d36a7aa99a0e043cb3e2c3d6ec6686e4af7f638b08601a4020a527f81c10a256b089fb2fbe2a1fc5917e0d221c0d06b8bb9095a6b26390634610f2034b99025ad70db4d84e08751841c2a70651220e32d1213a4b05ec9906"); +} + +TEST(HederaSigner, ProtoTestsTransferList) { + auto transferList = proto::TransferList(); + auto* to = transferList.add_accountamounts(); + to->set_amount(100000000); + auto accountIdTo = to->mutable_accountid(); + accountIdTo->set_shardnum(0); + accountIdTo->set_realmnum(0); + accountIdTo->set_accountnum(48462050); + + auto encoded = hex(transferList.SerializeAsString()); + ASSERT_EQ(encoded, "0a0c0a0518e2f18d17108084af5f"); +} + +TEST(HederaSigner, ProtoTestsDoubleTransferList) { + auto transferList = proto::TransferList(); + + { + auto* to = transferList.add_accountamounts(); + to->set_amount(100000000); + auto* accountIdTo = to->mutable_accountid(); + accountIdTo->set_shardnum(0); + accountIdTo->set_realmnum(0); + accountIdTo->set_accountnum(48462050); + } + + { + auto* from = transferList.add_accountamounts(); + from->set_amount(-100000000); + auto* accountIdFrom = from->mutable_accountid(); + accountIdFrom->set_shardnum(0); + accountIdFrom->set_realmnum(0); + accountIdFrom->set_accountnum(48694347); + } + + auto encoded = hex(transferList.SerializeAsString()); + ASSERT_EQ(encoded, "0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f"); +} + +TEST(HederaSigner, ProtoTestsCryptoTransfer) { + auto transferList = proto::TransferList(); + + { + auto* to = transferList.add_accountamounts(); + to->set_amount(100000000); + auto* accountIdTo = to->mutable_accountid(); + accountIdTo->set_shardnum(0); + accountIdTo->set_realmnum(0); + accountIdTo->set_accountnum(48462050); + } + + { + auto* from = transferList.add_accountamounts(); + from->set_amount(-100000000); + auto* accountIdFrom = from->mutable_accountid(); + accountIdFrom->set_shardnum(0); + accountIdFrom->set_realmnum(0); + accountIdFrom->set_accountnum(48694347); + } + + auto cryptoTransfer = proto::CryptoTransferTransactionBody(); + *cryptoTransfer.mutable_transfers() = transferList; + + auto encoded = hex(cryptoTransfer.SerializeAsString()); + ASSERT_EQ(encoded, "0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f"); +} + +TEST(HederaSigner, ProtoTestsTransactionBody) { + auto transferList = proto::TransferList(); + + { + auto* to = transferList.add_accountamounts(); + to->set_amount(100000000); + auto* accountIdTo = to->mutable_accountid(); + accountIdTo->set_shardnum(0); + accountIdTo->set_realmnum(0); + accountIdTo->set_accountnum(48462050); + } + + { + auto* from = transferList.add_accountamounts(); + from->set_amount(-100000000); + auto* accountIdFrom = from->mutable_accountid(); + accountIdFrom->set_shardnum(0); + accountIdFrom->set_realmnum(0); + accountIdFrom->set_accountnum(48694347); + } + + auto cryptoTransfer = proto::CryptoTransferTransactionBody(); + *cryptoTransfer.mutable_transfers() = transferList; + + auto transactionBody = proto::TransactionBody(); + *transactionBody.mutable_cryptotransfer() = cryptoTransfer; + transactionBody.set_transactionfee(100000000); + transactionBody.mutable_nodeaccountid()->set_accountnum(9); + transactionBody.mutable_transactionvalidduration()->set_seconds(120); + auto& transactionID = *transactionBody.mutable_transactionid(); + transactionID.mutable_accountid()->set_accountnum(48694347); + transactionID.mutable_transactionvalidstart()->set_nanos(749068449); + transactionID.mutable_transactionvalidstart()->set_seconds(1667222879); + + auto encoded = hex(transactionBody.SerializeAsString()); + + ASSERT_EQ(encoded, "0a150a0c08df9aff9a0610a1c197e502120518cb889c17120218091880c2d72f22020878721e0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f"); +} + +TEST(HederaSigner, ProtoTestsTransactionBodyWithMemo) { + auto transferList = proto::TransferList(); + { + auto* to = transferList.add_accountamounts(); + to->set_amount(100000000); + auto* accountIdTo = to->mutable_accountid(); + accountIdTo->set_shardnum(0); + accountIdTo->set_realmnum(0); + accountIdTo->set_accountnum(48462050); + } + + { + auto* from = transferList.add_accountamounts(); + from->set_amount(-100000000); + auto* accountIdFrom = from->mutable_accountid(); + accountIdFrom->set_shardnum(0); + accountIdFrom->set_realmnum(0); + accountIdFrom->set_accountnum(48694347); + } + + auto cryptoTransfer = proto::CryptoTransferTransactionBody(); + *cryptoTransfer.mutable_transfers() = transferList; + + auto transactionBody = proto::TransactionBody(); + transactionBody.set_memo("wallet core"); + *transactionBody.mutable_cryptotransfer() = cryptoTransfer; + transactionBody.set_transactionfee(100000000); + transactionBody.mutable_nodeaccountid()->set_accountnum(3); + transactionBody.mutable_transactionvalidduration()->set_seconds(120); + auto& transactionID = *transactionBody.mutable_transactionid(); + transactionID.mutable_accountid()->set_accountnum(48694347); + transactionID.mutable_transactionvalidstart()->set_nanos(942876449); + transactionID.mutable_transactionvalidstart()->set_seconds(1667215034); + + auto encoded = hex(transactionBody.SerializeAsString()); + ASSERT_EQ(encoded, "0a150a0c08baddfe9a0610a1ceccc103120518cb889c17120218031880c2d72f22020878320b77616c6c657420636f7265721e0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f"); +} + +} // namespace TW::Hedera::tests diff --git a/tests/chains/Hedera/TWAnySignerTests.cpp b/tests/chains/Hedera/TWAnySignerTests.cpp new file mode 100644 index 00000000000..a4958691315 --- /dev/null +++ b/tests/chains/Hedera/TWAnySignerTests.cpp @@ -0,0 +1,42 @@ +// Copyright © 2017-2022 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 +#include "Hedera/Signer.h" +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +namespace TW::Hedera::tests { + +TEST(TWAnySignerHedera, Sign) { + // Successfully broadcasted: https://hashscan.io/testnet/transaction/0.0.48694347-1667222879-749068449?t=1667222891.440398729&p=1 + Proto::SigningInput input; + auto privateKey = PrivateKey(parse_hex("e87a5584c0173263e138db689fdb2a7389025aaae7cb1a18a1017d76012130e8")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto* body = input.mutable_body(); + + *body->mutable_memo() = ""; + *body->mutable_nodeaccountid() = "0.0.9"; + body->set_transactionfee(100000000); + body->set_transactionvalidduration(120); + auto* transferMsg = body->mutable_transfer(); + transferMsg->set_from("0.0.48694347"); + transferMsg->set_to("0.0.48462050"); + transferMsg->set_amount(100000000); + + auto* transactionID = body->mutable_transactionid(); + transactionID->mutable_transactionvalidstart()->set_seconds(1667222879); + transactionID->mutable_transactionvalidstart()->set_nanos(749068449); + transactionID->set_accountid("0.0.48694347"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeHedera); + ASSERT_EQ(hex(output.encoded()), "0a440a150a0c08df9aff9a0610a1c197e502120518cb889c17120218091880c2d72f22020878721e0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f12660a640a205d3a70d08b2beafb72c7a68986b3ff819a306078b8c359d739e4966e82e6d40e1a40612589c3b15f1e3ed6084b5a3a5b1b81751578cac8d6c922f31731b3982a5bac80a22558b2197276f5bae49b62503a4d39448ceddbc5ef3ba9bee4c0f302f70c"); +} + +} diff --git a/tests/chains/Hedera/TWCoinTypeTests.cpp b/tests/chains/Hedera/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..ccaf1145074 --- /dev/null +++ b/tests/chains/Hedera/TWCoinTypeTests.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2022 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. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWHederaCoinType, TWCoinType) { + const auto coin = TWCoinTypeHedera; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0.0.19790-1666769504-858851231")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0.0.19790")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "hedera"); + assertStringsEqual(name, "Hedera"); + assertStringsEqual(symbol, "HBAR"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 8); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainHedera); + assertStringsEqual(txUrl, "https://hashscan.io/mainnet/transaction/0.0.19790-1666769504-858851231"); + assertStringsEqual(accUrl, "https://hashscan.io/mainnet/account/0.0.19790"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 5bd9acd5d63..eacfaa549e9 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -252,6 +252,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeAptos: EXPECT_EQ(address, "0xce2fd04ac9efa74f17595e5785e847a2399d7e637f5e8179244f76191f653276"); break; + case TWCoinTypeHedera: + EXPECT_EQ(address, "0.0.302a300506032b6570032100ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148"); + break; // no default branch here, intentionally, to better notice any missing coins } } diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp index 9bc90832bed..7430101c7da 100644 --- a/tests/common/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -10,6 +10,7 @@ #include "Bitcoin/CashAddress.h" #include "Bitcoin/SegwitAddress.h" #include "Ethereum/Address.h" +#include "Hedera/DER.h" #include "HexCoding.h" #include "PublicKey.h" #include "Hash.h" @@ -433,5 +434,27 @@ TEST(HDWallet, AptosKey) { } } +TEST(HDWallet, HederaKey) { + // https://github.com/hashgraph/hedera-sdk-js/blob/e0cd39c84ab189d59a6bcedcf16e4102d7bb8beb/packages/cryptography/test/unit/Mnemonic.js#L47 + { + const auto derivPath = "m/44'/3030'/0'/0'/0"; + HDWallet wallet = HDWallet("inmate flip alley wear offer often piece magnet surge toddler submit right radio absent pear floor belt raven price stove replace reduce plate home", ""); + { + const auto privateKey = wallet.getKey(TWCoinTypeHedera, DerivationPath(derivPath)); + EXPECT_EQ(Hedera::gHederaDerPrefixPrivate + hex(privateKey.bytes), "302e020100300506032b657004220420853f15aecd22706b105da1d709b4ac05b4906170c2b9c7495dff9af49e1391da"); + EXPECT_EQ(Hedera::gHederaDerPrefixPublic + hex(privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes), "302a300506032b6570032100b63b3815f453cf697b53b290b1d78e88c725d39bde52c34c79fb5b4c93894673"); + } + } + { + const auto derivPath = "m/44'/3030'/0'/0'/0"; + HDWallet wallet = HDWallet("walk gun glide frequent exhaust sugar siege prosper staff skill swarm label", ""); + { + const auto privateKey = wallet.getKey(TWCoinTypeHedera, DerivationPath(derivPath)); + EXPECT_EQ(Hedera::gHederaDerPrefixPrivate + hex(privateKey.bytes), "302e020100300506032b657004220420650c5120cbdc6244e3d10001eb27eea4dd3f80c331b3b6969fa434797d4edd50"); + EXPECT_EQ(Hedera::gHederaDerPrefixPublic + hex(privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes), "302a300506032b65700321007df3e1ab790b28de4706d36a7aa99a0e043cb3e2c3d6ec6686e4af7f638b0860"); + } + } +} + } // namespace diff --git a/tests/common/algorithm/string.cpp b/tests/common/algorithm/string.cpp new file mode 100644 index 00000000000..081e5e99939 --- /dev/null +++ b/tests/common/algorithm/string.cpp @@ -0,0 +1,19 @@ +// Copyright © 2017-2022 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 "algorithm/string.hpp" + +#include "gtest/gtest.h" + +namespace TW::tests { + TEST(Algorithm, StringSplit) { + auto splitted = TW::ssplit("0.0.1", '.'); + ASSERT_EQ(splitted.size(), 3uL); + ASSERT_EQ(splitted[0], "0"); + ASSERT_EQ(splitted[1], "0"); + ASSERT_EQ(splitted[2], "1"); + } +} diff --git a/tools/generate-files b/tools/generate-files index aa694add2cc..e6d74be818e 100755 --- a/tools/generate-files +++ b/tools/generate-files @@ -63,6 +63,7 @@ fi "$PROTOC" -I=$PREFIX/include -I=src/Tron/Protobuf --cpp_out=src/Tron/Protobuf src/Tron/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Zilliqa/Protobuf --cpp_out=src/Zilliqa/Protobuf src/Zilliqa/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Cosmos/Protobuf --cpp_out=src/Cosmos/Protobuf src/Cosmos/Protobuf/*.proto +"$PROTOC" -I=$PREFIX/include -I=src/Hedera/Protobuf --cpp_out=src/Hedera/Protobuf src/Hedera/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=tests/chains/Cosmos/Protobuf --cpp_out=tests/chains/Cosmos/Protobuf tests/chains/Cosmos/Protobuf/*.proto # Generate Proto interface file diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt index 379042f09e6..e7ea9f4ca0e 100644 --- a/wasm/CMakeLists.txt +++ b/wasm/CMakeLists.txt @@ -16,7 +16,7 @@ target_compile_options(${TARGET_NAME} PRIVATE "-Wall") set_target_properties(${TARGET_NAME} PROPERTIES - CXX_STANDARD 17 + CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON ) @@ -39,6 +39,6 @@ set_target_properties(${TARGET_NAME} set_target_properties(${TARGET_NAME} PROPERTIES - COMPILE_FLAGS "-O2 -sSTRICT -sUSE_BOOST_HEADERS=1" + COMPILE_FLAGS "-O2 -sSTRICT -sUSE_BOOST_HEADERS=1" LINK_FLAGS "--bind --no-entry --closure 1 -O2 -sSTRICT -sASSERTIONS -sMODULARIZE=1 -sALLOW_MEMORY_GROWTH=1 -sDYNAMIC_EXECUTION=0" ) diff --git a/wasm/tests/Blockchain/Hedera.test.ts b/wasm/tests/Blockchain/Hedera.test.ts new file mode 100644 index 00000000000..6c26bfadd6c --- /dev/null +++ b/wasm/tests/Blockchain/Hedera.test.ts @@ -0,0 +1,62 @@ +// Copyright © 2017-2022 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. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; +import { TW } from "../../dist"; +import Long = require("long"); + +describe("Hedera", () => { + + it("test address", () => { + const { PrivateKey, HexCoding, AnyAddress, CoinType, Curve } = globalThis.core; + const address = AnyAddress.createWithString("0.0.48694347", CoinType.hedera); + assert.equal(address.description(), "0.0.48694347"); + assert.equal(AnyAddress.isValid("0.0.48694347", CoinType.hedera), true); + assert.equal(AnyAddress.isValid("0.0.a", CoinType.hedera), false); + address.delete(); + }); + + it("test sign simple transfer Hedera", () => { + const { PrivateKey, HexCoding, AnySigner, AnyAddress, CoinType } = globalThis.core; + const transferMsg = TW.Hedera.Proto.TransferMessage.create({ + from: "0.0.48694347", + to: "0.0.48462050", + amount: new Long(100000000) + }) + + const transactionID = TW.Hedera.Proto.TransactionID.create({ + accountID: "0.0.48694347", + transactionValidStart: TW.Hedera.Proto.Timestamp.create({ + seconds: new Long(1667222879), + nanos: 749068449 + }) + }) + + const transactionBody = TW.Hedera.Proto.TransactionBody.create({ + memo: "", + nodeAccountID: "0.0.9", + transactionFee: new Long(100000000), + transactionValidDuration: new Long(120), + transfer: transferMsg, + transactionID: transactionID + }) + + const txDataInput = TW.Hedera.Proto.SigningInput.create({ + privateKey: PrivateKey.createWithData( + HexCoding.decode( + "0xe87a5584c0173263e138db689fdb2a7389025aaae7cb1a18a1017d76012130e8", + ), + ).data(), + body: transactionBody + }); + const input = TW.Hedera.Proto.SigningInput.encode(txDataInput).finish(); + const outputData = AnySigner.sign(input, CoinType.hedera); + const output = TW.Hedera.Proto.SigningOutput.decode(outputData); + assert.equal(HexCoding.encode(output.encoded), "0x0a440a150a0c08df9aff9a0610a1c197e502120518cb889c17120218091880c2d72f22020878721e0a1c0a0c0a0518e2f18d17108084af5f0a0c0a0518cb889c1710ff83af5f12660a640a205d3a70d08b2beafb72c7a68986b3ff819a306078b8c359d739e4966e82e6d40e1a40612589c3b15f1e3ed6084b5a3a5b1b81751578cac8d6c922f31731b3982a5bac80a22558b2197276f5bae49b62503a4d39448ceddbc5ef3ba9bee4c0f302f70c") + }); +}); From b69c9b276150fcc25a4c43e5ada61564a9d759b8 Mon Sep 17 00:00:00 2001 From: hewigovens <360470+hewigovens@users.noreply.github.com> Date: Wed, 9 Nov 2022 17:40:35 +0900 Subject: [PATCH 039/426] update oeth name (#2711) --- registry.json | 2 +- tests/chains/Optimism/TWCoinTypeTests.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/registry.json b/registry.json index fe76a261485..d2dcc9d2b74 100644 --- a/registry.json +++ b/registry.json @@ -2042,7 +2042,7 @@ { "id": "optimism", "name": "Optimism", - "displayName": "Optimistic Ethereum", + "displayName": "Optimism Ethereum", "coinId": 10000070, "slip44": 60, "symbol": "ETH", diff --git a/tests/chains/Optimism/TWCoinTypeTests.cpp b/tests/chains/Optimism/TWCoinTypeTests.cpp index 1cc6ff0e4c3..c95173bfe5f 100644 --- a/tests/chains/Optimism/TWCoinTypeTests.cpp +++ b/tests/chains/Optimism/TWCoinTypeTests.cpp @@ -9,7 +9,6 @@ #include #include - TEST(TWOptimismCoinType, TWCoinType) { auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOptimism)); auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x6fd99288be9bf71eb002bb31da10a4fb0fbbb3c45ae73693b212f49c9db7df8f")); @@ -26,5 +25,5 @@ TEST(TWOptimismCoinType, TWCoinType) { assertStringsEqual(txUrl, "https://optimistic.etherscan.io/tx/0x6fd99288be9bf71eb002bb31da10a4fb0fbbb3c45ae73693b212f49c9db7df8f"); assertStringsEqual(accUrl, "https://optimistic.etherscan.io/address/0x1f932361e31d206b4f6b2478123a9d0f8c761031"); assertStringsEqual(id, "optimism"); - assertStringsEqual(name, "Optimistic Ethereum"); + assertStringsEqual(name, "Optimism Ethereum"); } From 1009d570554d48c7ed530cca9394ec2b7fcf903d Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 10 Nov 2022 03:05:09 +0100 Subject: [PATCH 040/426] [License]: 3rd party licensing (#2709) * feat(license): add 3rd party license file * Update LICENSE-3RD-PARTY.txt Co-authored-by: hewigovens <360470+hewigovens@users.noreply.github.com> --- LICENSE-3RD-PARTY.txt | 850 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 850 insertions(+) create mode 100644 LICENSE-3RD-PARTY.txt diff --git a/LICENSE-3RD-PARTY.txt b/LICENSE-3RD-PARTY.txt new file mode 100644 index 00000000000..93b7fb0a22a --- /dev/null +++ b/LICENSE-3RD-PARTY.txt @@ -0,0 +1,850 @@ +3RD PARTY LICENSES + +Note that not all files in the wallet-core repository and in the released +software packages belong to the wallet-core project. For 3rd party files, +the individual licenses apply. + + +############################################################################# +LICENSE TEXTS +############################################################################# + +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +############################################################################# + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +############################################################################# + +Copyright 2008 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. + +############################################################################# + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +## Runtime Library Exception to the Apache 2.0 License: ## + + + As an exception, if you use this Software to compile your source code and + portions of this Software are embedded into the binary product as a result, + you may redistribute such product without providing attribution as would + otherwise be required by Sections 4(a), 4(b) and 4(d) of the License. + +############################################################################# + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +############################################################################# + +The MIT License (MIT) + +Copyright (c) 2013 Tomas Dzetkulic +Copyright (c) 2013 Pavol Rusnak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From c61a47d2862680c954369939f4951497705acb14 Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Thu, 10 Nov 2022 13:50:14 +0000 Subject: [PATCH 041/426] [Staking]: add ETH pooled staking tests (#2707) --- .../ethereum/TestEthereumTransactionSigner.kt | 62 +++++++++++ swift/Tests/Blockchains/EthereumTests.swift | 61 +++++++++++ tests/chains/Ethereum/TWAnySignerTests.cpp | 100 ++++++++++++++++++ 3 files changed, 223 insertions(+) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumTransactionSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumTransactionSigner.kt index ab3b38bf229..61a6b9a42df 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumTransactionSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumTransactionSigner.kt @@ -9,6 +9,8 @@ import wallet.core.java.AnySigner import wallet.core.jni.CoinType import wallet.core.jni.CoinType.ETHEREUM import wallet.core.jni.proto.Ethereum +import wallet.core.jni.EthereumAbi +import wallet.core.jni.EthereumAbiFunction import wallet.core.jni.proto.Ethereum.SigningOutput import wallet.core.jni.proto.Ethereum.TransactionMode import com.trustwallet.core.app.utils.Numeric @@ -147,6 +149,66 @@ class TestEthereumTransactionSigner { assertEquals(Numeric.toHexString(output.data.toByteArray()), "0xf242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000") } + @Test + fun testEthereumStakeRocketPool() { + val function = EthereumAbiFunction("deposit") + val signingInput = Ethereum.SigningInput.newBuilder() + signingInput.apply { + chainId = ByteString.copyFrom("01".toHexByteArray()) + nonce = ByteString.copyFrom("01".toHexByteArray()) + txMode = TransactionMode.Enveloped + gasPrice = ByteString.copyFrom("77541880".toHexByteArray()) // 2002000000 + gasLimit = ByteString.copyFrom("0320c8".toHexByteArray()) // 205000 + maxFeePerGas = ByteString.copyFrom("067ef83700".toHexByteArray()) // 27900000000 + maxInclusionFeePerGas = ByteString.copyFrom("3b9aca00".toHexByteArray()) // 1000000000 + toAddress = "0x2cac916b2a963bf162f076c0a8a4a8200bcfbfb4" // contract + privateKey = ByteString.copyFrom(PrivateKey("9f56448d33de406db1561aae15fce64bdf0e9706ff15c45d4409e8fcbfd1a498".toHexByteArray()).data()) + transaction = Ethereum.Transaction.newBuilder().apply { + transfer = Ethereum.Transaction.Transfer.newBuilder().apply { + amount = ByteString.copyFrom("2386f26fc10000".toHexByteArray()) // 0.01 ETH + data = ByteString.copyFrom(EthereumAbi.encode(function)) + }.build() + }.build() + } + + val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) + + // https://etherscan.io/tx/0xfeba0c579f3e964fbc4eafa500e86891b9f4113735b1364edd4433d765506f1e + assertEquals(Numeric.toHexString(output.r.toByteArray()), "0xfb39e5079d7a0598ec45785d73a06b91fe1db707b9c6a150c87ffce2492c66d6") + assertEquals(Numeric.toHexString(output.s.toByteArray()), "0x7fbd43a6f4733b2b4f98ad1bc4678ea2615f5edf56ad91408337adec2f07c0ac") + } + + @Test + fun testEthereumUnstakeRocketPool() { + val function = EthereumAbiFunction("burn") + function.addParamUInt256("0x21faa32ab2502b".toHexByteArray(), false) + + val signingInput = Ethereum.SigningInput.newBuilder() + signingInput.apply { + chainId = ByteString.copyFrom("01".toHexByteArray()) + nonce = ByteString.copyFrom("03".toHexByteArray()) + txMode = TransactionMode.Enveloped + gasPrice = ByteString.copyFrom("77541880".toHexByteArray()) // 2002000000 + gasLimit = ByteString.copyFrom("055730".toHexByteArray()) // 350000 + maxFeePerGas = ByteString.copyFrom("067ef83700".toHexByteArray()) // 27900000000 + maxInclusionFeePerGas = ByteString.copyFrom("3b9aca00".toHexByteArray()) // 1000000000 + toAddress = "0xae78736Cd615f374D3085123A210448E74Fc6393" // contract + privateKey = ByteString.copyFrom(PrivateKey("9f56448d33de406db1561aae15fce64bdf0e9706ff15c45d4409e8fcbfd1a498".toHexByteArray()).data()) + transaction = Ethereum.Transaction.newBuilder().apply { + contractGeneric = Ethereum.Transaction.ContractGeneric.newBuilder().apply { + amount = ByteString.copyFrom("00".toHexByteArray()) + data = ByteString.copyFrom(EthereumAbi.encode(function)) + }.build() + }.build() + } + + val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) + + // https://etherscan.io/tx/0x7fd3c0e9b8b309b4258baa7677c60f5e00e8db7b647fbe3a52adda25058a4b37 + assertEquals(Numeric.toHexString(output.r.toByteArray()), "0x1fc6e94908107584357799e952b4e3fb87f088aeb66d7930a7015643f19c9e7f") + assertEquals(Numeric.toHexString(output.s.toByteArray()), "0x2c56a0b70ff2e52bf374a3dcd404bc42317d5ca15d319f5e33665352eb48f06f") + } + @Test fun testSignJSON() { val json = """ diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index b16ba0bf7db..c3091e428b6 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -158,6 +158,67 @@ class EthereumTests: XCTestCase { XCTAssertEqual(output.data.hexString, "f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000") } + func testSignStakeRocketPool() { + let function = EthereumAbiFunction(name: "deposit") + + let input = EthereumSigningInput.with { + $0.chainID = Data(hexString: "01")! + $0.nonce = Data(hexString: "01")! + $0.txMode = .enveloped + $0.gasPrice = Data(hexString: "77541880")! // 2002000000 + $0.gasLimit = Data(hexString: "0320c8")! // 205000 + $0.maxFeePerGas = Data(hexString: "067ef83700")! // 27900000000 + $0.maxInclusionFeePerGas = Data(hexString: "3b9aca00")! // 1000000000 + $0.toAddress = "0x2cac916b2a963bf162f076c0a8a4a8200bcfbfb4" // contract + $0.privateKey = Data(hexString: "9f56448d33de406db1561aae15fce64bdf0e9706ff15c45d4409e8fcbfd1a498")! + + $0.transaction = EthereumTransaction.with { + $0.transfer = EthereumTransaction.Transfer.with { + $0.amount = Data(hexString: "2386f26fc10000")! // 0.01 ETH + $0.data = Data(hexString: EthereumAbi.encode(fn: function).hexString)! + } + } + } + let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + + // https://etherscan.io/tx/0xfeba0c579f3e964fbc4eafa500e86891b9f4113735b1364edd4433d765506f1e + XCTAssertEqual(output.r.hexString, "fb39e5079d7a0598ec45785d73a06b91fe1db707b9c6a150c87ffce2492c66d6") + XCTAssertEqual(output.v.hexString, "00") + XCTAssertEqual(output.s.hexString, "7fbd43a6f4733b2b4f98ad1bc4678ea2615f5edf56ad91408337adec2f07c0ac") + XCTAssertEqual(output.encoded.hexString, "02f8770101843b9aca0085067ef83700830320c8942cac916b2a963bf162f076c0a8a4a8200bcfbfb4872386f26fc1000084d0e30db0c080a0fb39e5079d7a0598ec45785d73a06b91fe1db707b9c6a150c87ffce2492c66d6a07fbd43a6f4733b2b4f98ad1bc4678ea2615f5edf56ad91408337adec2f07c0ac") + } + + func testSignUnstakeRocketPool() { + let function = EthereumAbiFunction(name: "burn") + function.addParamUInt256(val: Data(hexString: "0x21faa32ab2502b")!, isOutput: false) + + let input = EthereumSigningInput.with { + $0.chainID = Data(hexString: "01")! + $0.nonce = Data(hexString: "03")! + $0.txMode = .enveloped + $0.gasPrice = Data(hexString: "77541880")! // 2002000000 + $0.gasLimit = Data(hexString: "055730")! // 350000 + $0.maxFeePerGas = Data(hexString: "067ef83700")! // 27900000000 + $0.maxInclusionFeePerGas = Data(hexString: "3b9aca00")! // 1000000000 + $0.toAddress = "0xae78736Cd615f374D3085123A210448E74Fc6393" // contract + $0.privateKey = Data(hexString: "9f56448d33de406db1561aae15fce64bdf0e9706ff15c45d4409e8fcbfd1a498")! + + $0.transaction = EthereumTransaction.with { + $0.contractGeneric = EthereumTransaction.ContractGeneric.with { + $0.amount = Data(hexString: "00")! + $0.data = Data(hexString: EthereumAbi.encode(fn: function).hexString)! + } + } + } + let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + + // https://etherscan.io/tx/0x7fd3c0e9b8b309b4258baa7677c60f5e00e8db7b647fbe3a52adda25058a4b37 + XCTAssertEqual(output.r.hexString, "1fc6e94908107584357799e952b4e3fb87f088aeb66d7930a7015643f19c9e7f") + XCTAssertEqual(output.v.hexString, "00") + XCTAssertEqual(output.s.hexString, "2c56a0b70ff2e52bf374a3dcd404bc42317d5ca15d319f5e33665352eb48f06f") + XCTAssertEqual(output.encoded.hexString, "02f8900103843b9aca0085067ef837008305573094ae78736cd615f374d3085123a210448e74fc639380a442966c680000000000000000000000000000000000000000000000000021faa32ab2502bc080a01fc6e94908107584357799e952b4e3fb87f088aeb66d7930a7015643f19c9e7fa02c56a0b70ff2e52bf374a3dcd404bc42317d5ca15d319f5e33665352eb48f06f") + } + func testSignJSON() { let json = """ { diff --git a/tests/chains/Ethereum/TWAnySignerTests.cpp b/tests/chains/Ethereum/TWAnySignerTests.cpp index c50cef17583..bf4079e3f77 100644 --- a/tests/chains/Ethereum/TWAnySignerTests.cpp +++ b/tests/chains/Ethereum/TWAnySignerTests.cpp @@ -9,9 +9,11 @@ #include "HexCoding.h" #include "uint256.h" #include "proto/Ethereum.pb.h" +#include "Ethereum/Address.h" #include "Ethereum/ABI/Function.h" #include "Ethereum/ABI/ParamBase.h" #include "Ethereum/ABI/ParamAddress.h" +#include "PrivateKey.h" #include @@ -485,4 +487,102 @@ TEST(TWAnySignerEthereum, SignERC1155Transfer_1559) { ASSERT_EQ(hex(output.encoded()), "02f901500180847735940084b2d05e00830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b8e4f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000c080a0533df41dda5540c57257b7fe89c29cefff0155c333e063220df2bf9680fcc15aa036a844fd20de5a51de96ceaaf078558e87d86426a4a5d4b215ee1fd0fa397f8a"); } +TEST(TWAnySignerEthereum, StakeRocketPool) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(1)); + auto gasPrice = store(uint256_t(2002000000)); + auto gasLimit = store(uint256_t(205000)); + auto maxFeePerGas = store(uint256_t(27900000000)); + auto maxInclusionFeePerGas = store(uint256_t(1000000000)); + auto toAddress = "0x2cac916b2a963bf162f076c0a8a4a8200bcfbfb4"; + auto key = parse_hex("9f56448d33de406db1561aae15fce64bdf0e9706ff15c45d4409e8fcbfd1a498"); + const auto pk = PrivateKey(key); + + // 0.01 ETH + auto valueData = store(uint256_t(10000000000000000)); + + Data payload; + { + auto func = ABI::Function("deposit", std::vector>{ }); + func.encode(payload); + } + + + Proto::SigningInput input; + input.set_tx_mode(Proto::TransactionMode::Enveloped); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + + input.set_to_address(toAddress); + input.set_private_key(key.data(), key.size()); + + auto& transfer = *input.mutable_transaction()->mutable_transfer(); + transfer.set_amount(valueData.data(), valueData.size()); + transfer.set_data(payload.data(), payload.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + // https://etherscan.io/tx/0xfeba0c579f3e964fbc4eafa500e86891b9f4113735b1364edd4433d765506f1e + EXPECT_EQ(hex(output.r()), "fb39e5079d7a0598ec45785d73a06b91fe1db707b9c6a150c87ffce2492c66d6"); + EXPECT_EQ(hex(output.v()), "00"); + EXPECT_EQ(hex(output.s()), "7fbd43a6f4733b2b4f98ad1bc4678ea2615f5edf56ad91408337adec2f07c0ac"); + EXPECT_EQ(hex(output.encoded()), "02f8770101843b9aca0085067ef83700830320c8942cac916b2a963bf162f076c0a8a4a8200bcfbfb4872386f26fc1000084d0e30db0c080a0fb39e5079d7a0598ec45785d73a06b91fe1db707b9c6a150c87ffce2492c66d6a07fbd43a6f4733b2b4f98ad1bc4678ea2615f5edf56ad91408337adec2f07c0ac"); +} + +TEST(TWAnySignerEthereum, UnstakeRocketPool) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(3)); + auto gasPrice = store(uint256_t(2002000000)); + auto gasLimit = store(uint256_t(350000)); + auto maxFeePerGas = store(uint256_t(27900000000)); + auto maxInclusionFeePerGas = store(uint256_t(1000000000)); + auto toAddress = "0xae78736Cd615f374D3085123A210448E74Fc6393"; + auto key = parse_hex("9f56448d33de406db1561aae15fce64bdf0e9706ff15c45d4409e8fcbfd1a498"); + const auto pk = PrivateKey(key); + + auto valueData = store(uint256_t(0)); + + Data payload; + { + auto func = ABI::Function("burn", std::vector>{ + std::make_shared(uint256_t(0x21faa32ab2502b))}); + func.encode(payload); + } + + Proto::SigningInput input; + input.set_tx_mode(Proto::TransactionMode::Enveloped); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + + input.set_to_address(toAddress); + input.set_private_key(key.data(), key.size()); + + auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); + transfer.set_amount(valueData.data(), valueData.size()); + transfer.set_data(payload.data(), payload.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + // https://etherscan.io/tx/0x7fd3c0e9b8b309b4258baa7677c60f5e00e8db7b647fbe3a52adda25058a4b37 + EXPECT_EQ(hex(output.r()), "1fc6e94908107584357799e952b4e3fb87f088aeb66d7930a7015643f19c9e7f"); + EXPECT_EQ(hex(output.v()), "00"); + EXPECT_EQ(hex(output.s()), "2c56a0b70ff2e52bf374a3dcd404bc42317d5ca15d319f5e33665352eb48f06f"); + EXPECT_EQ(hex(output.encoded()), "02f8900103843b9aca0085067ef837008305573094ae78736cd615f374d3085123a210448e74fc639380a442966c680000000000000000000000000000000000000000000000000021faa32ab2502bc080a01fc6e94908107584357799e952b4e3fb87f088aeb66d7930a7015643f19c9e7fa02c56a0b70ff2e52bf374a3dcd404bc42317d5ca15d319f5e33665352eb48f06f"); +} + + } // namespace TW::Ethereum From c462a63014b970690279a4bfc5a8a3454d275330 Mon Sep 17 00:00:00 2001 From: Ahmad Heidari Date: Mon, 14 Nov 2022 11:37:28 +0300 Subject: [PATCH 042/426] Add Memes wallet to the list that use Wallet core (#2725) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 36b798cb3cd..2b39eab1cdc 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Projects using Trust Wallet Core. Add yours too! | [Alice](https://www.alicedapp.com/) | [Frontier](https://frontier.xyz/) | [Tokenary](https://tokenary.io/) +| [MemesWallet](https://planetmemes.com/) # Community From efca5eeef5ab79df88914c7be2c287317cd10234 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Mon, 14 Nov 2022 14:06:44 +0100 Subject: [PATCH 043/426] Support affiliate fee fields in memo (#2715) * Support affiliate fee fields in memo * Increase max OP_RETURN len to 80 bytes * Update iOS and android tests. * BitcoinScript test fix --- .../thorchain/TestTHORSwapSigning.kt | 10 +- docs/registry.md | 2 +- src/Bitcoin/Script.cpp | 2 +- src/THORChain/Swap.cpp | 33 +++- src/THORChain/Swap.h | 8 +- src/THORChain/TWSwap.cpp | 5 +- src/proto/THORChainSwap.proto | 9 ++ .../Blockchains/THORChainSwapTests.swift | 10 +- tests/chains/Bitcoin/BitcoinScriptTests.cpp | 11 +- tests/chains/THORChain/SwapTests.cpp | 142 ++++++++++++++++-- tests/chains/THORChain/TWSwapTests.cpp | 6 +- 11 files changed, 203 insertions(+), 35 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt index 2d1d3caf77e..e65151148a7 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt @@ -20,7 +20,7 @@ class TestTHORChainSwap { } @Test - fun testSwapEthBnb() { + fun testSwapEthBnbWithFee() { // prepare swap input val input = THORChainSwap.SwapInput.newBuilder() input.apply { @@ -36,15 +36,17 @@ class TestTHORChainSwap { routerAddress = "0x42A5Ed456650a09Dc10EBc6361A7480fDd61f27B" fromAmount = "50000000000000000" toAmountLimit = "600003" + affiliateFeeAddress = "tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy" + affiliateFeeRateBp = "10" } // serialize input val inputSerialized = input.build().toByteArray() - assertEquals(Numeric.toHexString(inputSerialized), "0x0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a1135303030303030303030303030303030304206363030303033") + assertEquals(Numeric.toHexString(inputSerialized), "0x0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a11353030303030303030303030303030303042063630303030334a2f7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c327463717952023130") // invoke swap val outputData = buildSwap(inputSerialized) - assertEquals(outputData.count(), 311) + assertEquals(outputData.count(), 375) // parse result in proto val outputProto = THORChainSwap.SwapOutput.newBuilder().mergeFrom(outputData) @@ -66,6 +68,6 @@ class TestTHORChainSwap { // sign and encode resulting input val output = AnySigner.sign(txInputFull, ETHEREUM, SigningOutput.parser()) - assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0xf90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000025a06ae104be3201baca38315352f81fac70ca4dd47339981914e64e91149813e780a066a3f0b2c44ddf5a96a38481274f623f552a593d723237d6742185f4885c0064") + assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0xf90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000026a027da86e94739f39e8b493f240eb043888a0dd6962a657963ff7fb26f10291ca8a03fed75d6703d5036402be4d0197432725e17fe6a5d3059abdcca74bd7a789cc8") } } diff --git a/docs/registry.md b/docs/registry.md index 3db48715d95..a1cecf40e66 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -76,7 +76,7 @@ This list is generated from [./registry.json](../registry.json) | 5718350 | Wanchain | WAN | | | | 5741564 | Waves | WAVES | | | | 10000025 | Cronos Chain | CRO | | | -| 10000070 | Optimistic Ethereum | ETH | | | +| 10000070 | Optimism Ethereum | ETH | | | | 10000100 | Gnosis Chain | xDAI | | | | 10000118 | Osmosis | OSMO | | | | 10000145 | Smart Bitcoin Cash | BCH | | | diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index 7000814c396..82c26bf8660 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -259,7 +259,7 @@ Script Script::buildPayToV1WitnessProgram(const Data& publicKey) { } Script Script::buildOpReturnScript(const Data& data) { - static const size_t MaxOpReturnLength = 64; + static const size_t MaxOpReturnLength = 80; Script script; script.bytes.push_back(OP_RETURN); size_t size = std::min(data.size(), MaxOpReturnLength); diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index 17d47ebf51e..b799b645023 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -54,13 +54,33 @@ std::string chainName(Chain chain) { } } -std::string Swap::buildMemo(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& toAddress, uint64_t limit) { +std::string Swap::buildMemo(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& toAddress, uint64_t limit, const std::string& feeAddress, std::optional feeRate, const std::string& extra) { std::string prefix = "SWAP"; if (toChain == Chain::ETH) { prefix = "="; } const auto toCoinToken = (!toTokenId.empty() && toTokenId != "0x0000000000000000000000000000000000000000") ? toTokenId : toSymbol; - return prefix + ":" + chainName(toChain) + "." + toCoinToken + ":" + toAddress + ":" + std::to_string(limit); + std::stringstream memo; + memo << prefix + ":" + chainName(toChain) + "." + toCoinToken + ":" + toAddress + ":" + std::to_string(limit); + + if (!feeAddress.empty() || feeRate.has_value() || !extra.empty()) { + memo << ":"; + if (!feeAddress.empty()) { + memo << feeAddress; + } + if (feeRate.has_value() || !extra.empty()) { + memo << ":"; + if (feeRate.has_value()) { + memo << std::to_string(feeRate.value()); + } + if (!extra.empty()) { + memo << ":"; + memo << extra; + } + } + } + + return memo.str(); } bool validateAddress(Chain chain, const std::string& address) { @@ -77,7 +97,10 @@ std::tuple Swap::build( const std::string& vaultAddress, const std::string& routerAddress, const std::string& fromAmount, - const std::string& toAmountLimit + const std::string& toAmountLimit, + const std::string& affFeeAddress, + const std::string& affFeeRate, + const std::string& extraMemo ) { if (!validateAddress(fromChain, fromAddress)) { return std::make_tuple({}, static_cast(Proto::ErrorCode::Error_Invalid_from_address), "Invalid from address"); @@ -88,7 +111,9 @@ std::tuple Swap::build( uint64_t fromAmountNum = std::atoll(fromAmount.c_str()); uint64_t toAmountLimitNum = std::atoll(toAmountLimit.c_str()); - const auto memo = buildMemo(toChain, toSymbol, toTokenId, toAddress, toAmountLimitNum); + std::optional feeRateNum = affFeeRate.empty() ? std::nullopt : std::optional(std::atoll(affFeeRate.c_str())); + + const auto memo = buildMemo(toChain, toSymbol, toTokenId, toAddress, toAmountLimitNum, affFeeAddress, feeRateNum, extraMemo); switch (fromChain) { case Chain::BTC: { diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h index 723b4ee83f2..6f74f047c0e 100644 --- a/src/THORChain/Swap.h +++ b/src/THORChain/Swap.h @@ -10,6 +10,7 @@ #include #include +#include namespace TW::THORChainSwap { @@ -37,7 +38,10 @@ class Swap { const std::string& vaultAddress, // ThorChainSwap vault, on the source chain. Should be queried afresh, as it may change const std::string& routerAddress, // ThorChain router, only in case of Ethereum source network const std::string& fromAmount, // The source amount, as integer in the smallest native unit of the chain - const std::string& toAmountLimit // The minimum accepted destination amount. Actual destination amount will depend on current rates, limit amount can be used to prevent using very unfavorable rates. + const std::string& toAmountLimit, // The minimum accepted destination amount. Actual destination amount will depend on current rates, limit amount can be used to prevent using very unfavorable rates. + const std::string& affFeeAddress = "", // Optional affiliate fee destination address. A Rune address. + const std::string& affFeeRate = "", // Optional affiliate fee, percentage base points, e.g. 100 means 1%, 0 - 1000, as string. + const std::string& extraMemo = "" // Optional extra custom memo, reserved for later use. ); protected: @@ -46,7 +50,7 @@ class Swap { static std::pair buildBinance(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& fromAddress, const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out); public: - static std::string buildMemo(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& toAddress, uint64_t limit); + static std::string buildMemo(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& toAddress, uint64_t limit, const std::string& feeAddress, std::optional feeRate, const std::string& extra); }; } // namespace TW diff --git a/src/THORChain/TWSwap.cpp b/src/THORChain/TWSwap.cpp index 9f1341c50fe..2b9c70c567a 100644 --- a/src/THORChain/TWSwap.cpp +++ b/src/THORChain/TWSwap.cpp @@ -34,7 +34,10 @@ TWData* _Nonnull TWTHORChainSwapBuildSwap(TWData* _Nonnull input) { inputProto.vault_address(), inputProto.router_address(), inputProto.from_amount(), - inputProto.to_amount_limit()); + inputProto.to_amount_limit(), + inputProto.affiliate_fee_address(), + inputProto.affiliate_fee_rate_bp(), + inputProto.extra_memo()); outputProto.set_from_chain(fromChain); outputProto.set_to_chain(toChain); diff --git a/src/proto/THORChainSwap.proto b/src/proto/THORChainSwap.proto index 7f3a20396c0..90ef10fcb19 100644 --- a/src/proto/THORChainSwap.proto +++ b/src/proto/THORChainSwap.proto @@ -75,6 +75,15 @@ message SwapInput { // The minimum accepted destination amount. Actual destination amount will depend on current rates, limit amount can be used to prevent using very unfavorable rates. string to_amount_limit = 8; + + // Optional affiliate fee destination address. A Rune address. + string affiliate_fee_address = 9; + + // Optional affiliate fee, percentage base points, e.g. 100 means 1%, 0 - 1000, as string. Empty means to ignore it. + string affiliate_fee_rate_bp = 10; + + // Optional extra custom memo, reserved for later use. + string extra_memo = 11; } // Result of the swap, a SigningInput struct for the specific chain diff --git a/swift/Tests/Blockchains/THORChainSwapTests.swift b/swift/Tests/Blockchains/THORChainSwapTests.swift index cb51dd73711..56a1d6a851d 100644 --- a/swift/Tests/Blockchains/THORChainSwapTests.swift +++ b/swift/Tests/Blockchains/THORChainSwapTests.swift @@ -9,7 +9,7 @@ import WalletCore class THORSwapTests: XCTestCase { - func testSignerEthBnb() throws { + func testSignerEthBnbWithFee() throws { // prepare swap input let input = THORChainSwapSwapInput.with { $0.fromChain = .eth @@ -24,15 +24,17 @@ class THORSwapTests: XCTestCase { $0.routerAddress = "0x42A5Ed456650a09Dc10EBc6361A7480fDd61f27B" $0.fromAmount = "50000000000000000" $0.toAmountLimit = "600003" + $0.affiliateFeeAddress = "tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy" + $0.affiliateFeeRateBp = "10" } // serialize input let inputSerialized = try input.serializedData() - XCTAssertEqual(inputSerialized.hexString, "0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a1135303030303030303030303030303030304206363030303033") + XCTAssertEqual(inputSerialized.hexString, "0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a11353030303030303030303030303030303042063630303030334a2f7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c327463717952023130") // invoke swap let outputData = THORChainSwap.buildSwap(input: inputSerialized) - XCTAssertEqual(outputData.count, 311) + XCTAssertEqual(outputData.count, 375) // parse result in proto let outputProto = try THORChainSwapSwapOutput(serializedData: outputData) @@ -51,7 +53,7 @@ class THORSwapTests: XCTestCase { // sign and encode resulting input let output: EthereumSigningOutput = AnySigner.sign(input: txInput, coin: .ethereum) - XCTAssertEqual(output.encoded.hexString, "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000025a06ae104be3201baca38315352f81fac70ca4dd47339981914e64e91149813e780a066a3f0b2c44ddf5a96a38481274f623f552a593d723237d6742185f4885c0064") + XCTAssertEqual(output.encoded.hexString, "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000026a027da86e94739f39e8b493f240eb043888a0dd6962a657963ff7fb26f10291ca8a03fed75d6703d5036402be4d0197432725e17fe6a5d3059abdcca74bd7a789cc8") } func testSignerBnbBtc() throws { diff --git a/tests/chains/Bitcoin/BitcoinScriptTests.cpp b/tests/chains/Bitcoin/BitcoinScriptTests.cpp index 66c50b9701e..57d53a23310 100644 --- a/tests/chains/Bitcoin/BitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/BitcoinScriptTests.cpp @@ -313,11 +313,18 @@ TEST(BitcoinScript, OpReturn) { Script script = Script::buildOpReturnScript(data); EXPECT_EQ(hex(script.bytes), "6a3b535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a"); } + { + Data data = Data(69); + data.push_back(0xab); + Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(hex(script.bytes), "6a46000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab"); + } { // too long, truncated - Data data = Data(70); + Data data = Data(89); + data.push_back(0xab); Script script = Script::buildOpReturnScript(data); - EXPECT_EQ(hex(script.bytes), "6a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + EXPECT_EQ(hex(script.bytes), "6a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } } diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 1f0738595e4..e8fe49bd51c 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -85,11 +85,11 @@ TEST(THORChainSwap, SwapBtcEth) { "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" "03" // outputs "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" - "609deb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "42" "6a403d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030" + "d49ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "49" "6a473d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313430303030303030303030303030303030" // witness "02" - "47" "304402205de19c68b5ea683b9d701d45b09f96658088db76e59ad27bd7b8383ee5d484ec0220245459a4d6d679d8b457564fccc7ecc5831c7ebed49e0366c65ac031e8a5b49201" + "48" "3045022100a67f84cbde5affbb46ffff2b33c1453ff2de70ef990fc974175d9a609e5a87ed0220589c57d958208f866c9477c7d6c9075dea4c58622debb02eab85032b8b6d373001" "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" "00000000" // nLockTime ); @@ -139,11 +139,11 @@ TEST(THORChainSwap, SwapBtcBnb) { "eb48da786cbd9430bf5ef3d1d3bc7206a4182fd7d5ac3f4e8d05754c3a5cae8e" "00000000" "00" "" "fcffffff" "03" // outputs "400d030000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" - "108d030000000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "42" "6a40535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3134303030303030" + "c08c030000000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "43" "6a41535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030" // witness "02" - "48" "30450221008427ac07af830abbf9f2e1b182096d9faefc9e5b4324786ec68386579b05d02102204fd062817a59255d62aba24b1b0c66bc070d0ddbb70bf130a6159cc057e7f6c801210" + "47" "3044022071616db5a7fc9e01307ed90eb9a0a30f1441b528ba7631dc8cc8d37448369eed02202e338a0bf857c8dd096173f921fbe0d7d5f154197ab2ca7800079432f6ba486301210" "21" "e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" "00000000" // nLockTime ); @@ -365,14 +365,130 @@ TEST(THORChainSwap, SwapBnbBnbToken) { // https://explorer.binance.org/tx/60C54C9F253B89C36A2788AB66951045E8AC5F5729597CB6C64A13013A7A54CC } +TEST(THORChainSwap, SwapBtcEthWithAffFee) { + auto res = Swap::build(Chain::BTC, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultBtc, "", "1000000", "140000000000000000", "tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy", "10"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a7a3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130"); + + auto tx = Bitcoin::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.amount(), 1000000); + EXPECT_EQ(tx.to_address(), VaultBtc); + EXPECT_EQ(tx.change_address(), Address1Btc); + EXPECT_EQ(tx.output_op_return(), "=:ETH.ETH:0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7:140000000000000000:tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy:10"); + EXPECT_EQ(tx.coin_type(), 0ul); + EXPECT_EQ(tx.private_key_size(), 0); + EXPECT_FALSE(tx.has_plan()); + + // set few fields before signing + tx.set_byte_fee(20); + EXPECT_EQ(Bitcoin::SegwitAddress(PrivateKey(TestKey1Btc).getPublicKey(TWPublicKeyTypeSECP256k1), "bc").string(), Address1Btc); + tx.add_private_key(TestKey1Btc.data(), TestKey1Btc.size()); + auto& utxo = *tx.add_utxo(); + Data utxoHash = parse_hex("1234000000000000000000000000000000000000000000000000000000005678"); + utxo.mutable_out_point()->set_hash(utxoHash.data(), utxoHash.size()); + utxo.mutable_out_point()->set_index(0); + utxo.mutable_out_point()->set_sequence(UINT32_MAX); + auto utxoScript = Bitcoin::Script::lockScriptForAddress(Address1Btc, TWCoinTypeBitcoin); + utxo.set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo.set_amount(50000000); + tx.set_use_max_amount(false); + + // sign and encode resulting input + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), 0); + EXPECT_EQ(hex(output.encoded()), // printed using prettyPrintTransaction + "01000000" // version + "0001" // marker & flag + "01" // inputs + "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" + "03" // outputs + "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" + "209ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "52" "6a503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7474686f7231716c" + // witness + "02" + "48" "3045022100d655ed4d84a5308f206039ff6125e456134e606fe620a81bc86c142a024226eb02207e53622e7572b7099b73af60ffdcc1e1e983f7ccca07dc3e3d496efb2270ada701" + "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" + "00000000" // nLockTime + ); +} + +TEST(THORChainSwap, SwapEthBnbWithAffFee) { + auto res = Swap::build(Chain::ETH, Chain::BNB, Address1Eth, "BNB", "", Address1Bnb, VaultEth, RouterEth, "50000000000000000", "600003", "tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy", "10"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "0a01001201002201002a0100422a30783432413545643435363635306130394463313045426336333631413734383066446436316632374252b30232b0020a07b1a2bc2ec5000012a4021fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130000000000000000000000000000000"); + + auto tx = Ethereum::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.to_address(), RouterEth); + ASSERT_TRUE(tx.transaction().has_contract_generic()); + + Data vaultAddressBin = SwapTest_ethAddressStringToData(VaultEth); + EXPECT_EQ(hex(vaultAddressBin), "1091c4de6a3cf09cda00abdaed42c7c3b69c83ec"); + auto func = Ethereum::ABI::Function("deposit", std::vector>{ + std::make_shared(vaultAddressBin), + std::make_shared(parse_hex("0000000000000000000000000000000000000000")), + std::make_shared(uint256_t(50000000000000000)), + std::make_shared("SWAP:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:600003:tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy:10") + }); + Data payload; + func.encode(payload); + EXPECT_EQ(hex(payload), "1fece7b4" + "0000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec" + "0000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000b1a2bc2ec50000" + "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000071" + "535741503a424e422e424e423a626e6231757334377764686678303863683937" + "7a6475656833783375356d757266727833306a656372783a3630303030333a74" + "74686f7231716c3274637179727173676e716c32746371796a326e386b66646d" + "74396c6830797a716c32746371793a3130000000000000000000000000000000"); + + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "b1a2bc2ec50000"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); + + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set few fields before signing + auto chainId = store(uint256_t(1)); + tx.set_chain_id(chainId.data(), chainId.size()); + auto nonce = store(uint256_t(3)); + tx.set_nonce(nonce.data(), nonce.size()); + auto gasPrice = store(uint256_t(30000000000)); + tx.set_gas_price(gasPrice.data(), gasPrice.size()); + auto gasLimit = store(uint256_t(80000)); + tx.set_gas_limit(gasLimit.data(), gasLimit.size()); + tx.set_private_key(""); + tx.set_private_key(TestKey1Eth.data(), TestKey1Eth.size()); + + // sign and encode resulting input + Ethereum::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeEthereum); + EXPECT_EQ(hex(output.encoded()), "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000026a027da86e94739f39e8b493f240eb043888a0dd6962a657963ff7fb26f10291ca8a03fed75d6703d5036402be4d0197432725e17fe6a5d3059abdcca74bd7a789cc8"); +} + TEST(THORChainSwap, Memo) { - EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234), "SWAP:BTC.BTC:btc123:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "", "bnb123", 1234), "SWAP:BNB.BNB:bnb123:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "", "0xaabbccdd", 1234), "=:ETH.ETH:0xaabbccdd:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "", "0xaabbccdd", 1234), "=:ETH.ETH:0xaabbccdd:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "0x0000000000000000000000000000000000000000", "0xaabbccdd", 1234), "=:ETH.ETH:0xaabbccdd:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "0x4B0F1812e5Df2A09796481Ff14017e6005508003", "0xaabbccdd", 1234), "=:ETH.0x4B0F1812e5Df2A09796481Ff14017e6005508003:0xaabbccdd:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "TWT-8C2", "bnb123", 1234), "SWAP:BNB.TWT-8C2:bnb123:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "", std::nullopt, ""), "SWAP:BTC.BTC:btc123:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "", "bnb123", 1234, "", std::nullopt, ""), "SWAP:BNB.BNB:bnb123:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "", "0xaabbccdd", 1234, "", std::nullopt, ""), "=:ETH.ETH:0xaabbccdd:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "", "0xaabbccdd", 1234, "", std::nullopt, ""), "=:ETH.ETH:0xaabbccdd:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "0x0000000000000000000000000000000000000000", "0xaabbccdd", 1234, "", std::nullopt, ""), "=:ETH.ETH:0xaabbccdd:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "0x4B0F1812e5Df2A09796481Ff14017e6005508003", "0xaabbccdd", 1234, "", std::nullopt, ""), "=:ETH.0x4B0F1812e5Df2A09796481Ff14017e6005508003:0xaabbccdd:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "TWT-8C2", "bnb123", 1234, "", std::nullopt, ""), "SWAP:BNB.TWT-8C2:bnb123:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "feeaddr", std::nullopt, ""), "SWAP:BTC.BTC:btc123:1234:feeaddr"); + EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "feeaddr", 10, ""), "SWAP:BTC.BTC:btc123:1234:feeaddr:10"); + EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "feeaddr", 10, "extramemo"), "SWAP:BTC.BTC:btc123:1234:feeaddr:10:extramemo"); + EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "feeaddr", 0, ""), "SWAP:BTC.BTC:btc123:1234:feeaddr:0"); + EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "", 10, ""), "SWAP:BTC.BTC:btc123:1234::10"); + EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "", std::nullopt, "extramemo"), "SWAP:BTC.BTC:btc123:1234:::extramemo"); } TEST(THORChainSwap, WrongFromAddress) { diff --git a/tests/chains/THORChain/TWSwapTests.cpp b/tests/chains/THORChain/TWSwapTests.cpp index ea266cb6584..c82b276e299 100644 --- a/tests/chains/THORChain/TWSwapTests.cpp +++ b/tests/chains/THORChain/TWSwapTests.cpp @@ -105,11 +105,11 @@ TEST(TWTHORChainSwap, SwapBtcToEth) { "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" "03" // outputs "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" - "609deb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "42" "6a403d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030" + "d49ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "49" "6a473d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313430303030303030303030303030303030" // witness "02" - "47" "304402205de19c68b5ea683b9d701d45b09f96658088db76e59ad27bd7b8383ee5d484ec0220245459a4d6d679d8b457564fccc7ecc5831c7ebed49e0366c65ac031e8a5b49201" + "48" "3045022100a67f84cbde5affbb46ffff2b33c1453ff2de70ef990fc974175d9a609e5a87ed0220589c57d958208f866c9477c7d6c9075dea4c58622debb02eab85032b8b6d373001" "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" "00000000" // nLockTime ); From 9378130d26ec263abb2403920750f1a55f154a10 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Fri, 18 Nov 2022 10:36:38 +0100 Subject: [PATCH 044/426] BitcoinScript/ThorSwap: Treat as error cases when memo is too long (do not truncate) (#2741) --- src/Bitcoin/Script.cpp | 10 ++-- src/Bitcoin/Script.h | 4 +- src/Bitcoin/TransactionBuilder.h | 18 +++++-- src/Bitcoin/TransactionSigner.cpp | 12 ++++- src/Zcash/TransactionBuilder.h | 11 ++-- tests/chains/Bitcoin/BitcoinScriptTests.cpp | 17 +++--- .../chains/Bitcoin/TWBitcoinSigningTests.cpp | 2 +- tests/chains/THORChain/SwapTests.cpp | 52 ++++++++++++++++--- 8 files changed, 95 insertions(+), 31 deletions(-) diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index 82c26bf8660..7c8b8da75f4 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -260,11 +260,15 @@ Script Script::buildPayToV1WitnessProgram(const Data& publicKey) { Script Script::buildOpReturnScript(const Data& data) { static const size_t MaxOpReturnLength = 80; + if (data.size() > MaxOpReturnLength) { + // data too long, cannot fit, fail (do not truncate) + return Script(); + } + assert(data.size() <= MaxOpReturnLength); Script script; script.bytes.push_back(OP_RETURN); - size_t size = std::min(data.size(), MaxOpReturnLength); - script.bytes.push_back(static_cast(size)); - script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + size); + script.bytes.push_back(static_cast(data.size())); + script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + data.size()); return script; } diff --git a/src/Bitcoin/Script.h b/src/Bitcoin/Script.h index 39cc5c874fc..f4c27c32517 100644 --- a/src/Bitcoin/Script.h +++ b/src/Bitcoin/Script.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -91,7 +91,7 @@ class Script { /// Builds a V1 pay-to-witness-program script, P2TR (from a 32-byte Schnorr public key). static Script buildPayToV1WitnessProgram(const Data& publicKey); - /// Builds an OP_RETURN script with given data + /// Builds an OP_RETURN script with given data. Returns empty script on error, if data is too long (>80). static Script buildOpReturnScript(const Data& data); /// Builds a appropriate lock script for the given diff --git a/src/Bitcoin/TransactionBuilder.h b/src/Bitcoin/TransactionBuilder.h index b2c9feeb8c0..caa6bef7b97 100644 --- a/src/Bitcoin/TransactionBuilder.h +++ b/src/Bitcoin/TransactionBuilder.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,6 +10,7 @@ #include "Transaction.h" #include "TransactionPlan.h" #include "InputSelector.h" +#include "../Result.h" #include "../proto/Bitcoin.pb.h" #include @@ -25,18 +26,22 @@ class TransactionBuilder { /// Builds a transaction with the selected input UTXOs, and one main output and an optional change output. template - static Transaction build(const TransactionPlan& plan, const std::string& toAddress, + static Result build(const TransactionPlan& plan, const std::string& toAddress, const std::string& changeAddress, enum TWCoinType coin, uint32_t lockTime) { Transaction tx; tx.lockTime = lockTime; auto outputTo = prepareOutputWithScript(toAddress, plan.amount, coin); - if (!outputTo.has_value()) { return {}; } + if (!outputTo.has_value()) { + return Result::failure(Common::Proto::Error_invalid_address); + } tx.outputs.push_back(outputTo.value()); if (plan.change > 0) { auto outputChange = prepareOutputWithScript(changeAddress, plan.change, coin); - if (!outputChange.has_value()) { return {}; } + if (!outputChange.has_value()) { + return Result::failure(Common::Proto::Error_invalid_address); + } tx.outputs.push_back(outputChange.value()); } @@ -48,10 +53,13 @@ class TransactionBuilder { // Optional OP_RETURN output if (plan.outputOpReturn.size() > 0) { auto lockingScriptOpReturn = Script::buildOpReturnScript(plan.outputOpReturn); + if (lockingScriptOpReturn.bytes.size() == 0) { + return Result::failure(Common::Proto::Error_invalid_memo); + } tx.outputs.emplace_back(0, lockingScriptOpReturn); } - return tx; + return Result(tx); } /// Prepares a TransactionOutput with given address and amount, prepares script for it diff --git a/src/Bitcoin/TransactionSigner.cpp b/src/Bitcoin/TransactionSigner.cpp index 0e87be108e8..c67a4b2de08 100644 --- a/src/Bitcoin/TransactionSigner.cpp +++ b/src/Bitcoin/TransactionSigner.cpp @@ -27,7 +27,11 @@ Result TransactionSigner(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime); + auto tx_result = TransactionBuilder::template build(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime); + if (!tx_result) { + return Result::failure(tx_result.error()); + } + Transaction transaction = tx_result.payload(); SigningMode signingMode = estimationMode ? SigningMode_SizeEstimationOnly : optionalExternalSigs.has_value() ? SigningMode_External : SigningMode_Normal; @@ -43,7 +47,11 @@ Result TransactionSigner(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime); + auto tx_result = TransactionBuilder::template build(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime); + if (!tx_result) { + return Result::failure(tx_result.error()); + } + Transaction transaction = tx_result.payload(); SignatureBuilder signer(std::move(input), plan, transaction, SigningMode_HashOnly); auto signResult = signer.sign(); if (!signResult) { diff --git a/src/Zcash/TransactionBuilder.h b/src/Zcash/TransactionBuilder.h index 1c5cd9fde1e..02c8db82d57 100644 --- a/src/Zcash/TransactionBuilder.h +++ b/src/Zcash/TransactionBuilder.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,6 +11,7 @@ #include "../Bitcoin/TransactionPlan.h" #include "../proto/Bitcoin.pb.h" #include "../HexCoding.h" +#include "../Result.h" #include #include @@ -25,18 +26,20 @@ struct TransactionBuilder { /// Builds a transaction by selecting UTXOs and calculating fees. template - static Transaction build(const Bitcoin::TransactionPlan& plan, const std::string& toAddress, + static Result build(const Bitcoin::TransactionPlan& plan, const std::string& toAddress, const std::string& changeAddress, enum TWCoinType coin, uint32_t lockTime) { coin = TWCoinTypeZcash; - Transaction tx = + auto tx_result = Bitcoin::TransactionBuilder::build(plan, toAddress, changeAddress, coin, lockTime); + if (!tx_result) { return Result::failure(tx_result.error()); } + Transaction tx = tx_result.payload(); // if not set, always use latest consensus branch id if (plan.branchId.empty()) { std::copy(BlossomBranchID.begin(), BlossomBranchID.end(), tx.branchId.begin()); } else { std::copy(plan.branchId.begin(), plan.branchId.end(), tx.branchId.begin()); } - return tx; + return Result(tx); } }; diff --git a/tests/chains/Bitcoin/BitcoinScriptTests.cpp b/tests/chains/Bitcoin/BitcoinScriptTests.cpp index 57d53a23310..0bdc3d92768 100644 --- a/tests/chains/Bitcoin/BitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/BitcoinScriptTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -319,13 +319,14 @@ TEST(BitcoinScript, OpReturn) { Script script = Script::buildOpReturnScript(data); EXPECT_EQ(hex(script.bytes), "6a46000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab"); } - { - // too long, truncated - Data data = Data(89); - data.push_back(0xab); - Script script = Script::buildOpReturnScript(data); - EXPECT_EQ(hex(script.bytes), "6a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } +} + +TEST(BitcoinScript, OpReturnTooLong) { + // too long, truncated + Data data = Data(89); + data.push_back(0xab); + Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(hex(script.bytes), ""); } TEST(BitcoinTransactionSigner, PushAllEmpty) { diff --git a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp index f2c26b2691c..bdf001a94a8 100644 --- a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp @@ -1071,7 +1071,7 @@ TEST(BitcoinSigning, Sign_NegativeInvalidAddress) { auto result = TransactionSigner::sign(input); ASSERT_FALSE(result); - EXPECT_EQ(result.error(), Common::Proto::Error_missing_input_utxos); + EXPECT_EQ(result.error(), Common::Proto::Error_invalid_address); } TEST(BitcoinSigning, Plan_10input_MaxAmount) { diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index e8fe49bd51c..1f2e5fc5a37 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -366,10 +366,10 @@ TEST(THORChainSwap, SwapBnbBnbToken) { } TEST(THORChainSwap, SwapBtcEthWithAffFee) { - auto res = Swap::build(Chain::BTC, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultBtc, "", "1000000", "140000000000000000", "tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy", "10"); + auto res = Swap::build(Chain::BTC, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultBtc, "", "1000000", "140000000000000000", "thrnm", "10"); ASSERT_EQ(std::get<1>(res), 0); ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a7a3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130"); + EXPECT_EQ(hex(std::get<0>(res)), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130"); auto tx = Bitcoin::Proto::SigningInput(); ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); @@ -378,7 +378,7 @@ TEST(THORChainSwap, SwapBtcEthWithAffFee) { EXPECT_EQ(tx.amount(), 1000000); EXPECT_EQ(tx.to_address(), VaultBtc); EXPECT_EQ(tx.change_address(), Address1Btc); - EXPECT_EQ(tx.output_op_return(), "=:ETH.ETH:0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7:140000000000000000:tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy:10"); + EXPECT_EQ(tx.output_op_return(), "=:ETH.ETH:0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7:140000000000000000:thrnm:10"); EXPECT_EQ(tx.coin_type(), 0ul); EXPECT_EQ(tx.private_key_size(), 0); EXPECT_FALSE(tx.has_plan()); @@ -409,10 +409,10 @@ TEST(THORChainSwap, SwapBtcEthWithAffFee) { "03" // outputs "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" "209ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "52" "6a503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7474686f7231716c" + "0000000000000000" "52" "6a503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130" // witness "02" - "48" "3045022100d655ed4d84a5308f206039ff6125e456134e606fe620a81bc86c142a024226eb02207e53622e7572b7099b73af60ffdcc1e1e983f7ccca07dc3e3d496efb2270ada701" + "48" "3045022100801dc46b14eb1b630050d48db27557b66254c3b9439909d864747eafbb12f7e902201fdf5433eaf4968a5596989330bc56513b5769c5c9fd77c41f9bdb55cf8202cd01" "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" "00000000" // nLockTime ); @@ -475,6 +475,46 @@ TEST(THORChainSwap, SwapEthBnbWithAffFee) { EXPECT_EQ(hex(output.encoded()), "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000026a027da86e94739f39e8b493f240eb043888a0dd6962a657963ff7fb26f10291ca8a03fed75d6703d5036402be4d0197432725e17fe6a5d3059abdcca74bd7a789cc8"); } +TEST(THORChainSwap, SwapBtcNegativeMemoTooLong) { + auto res = Swap::build(Chain::BTC, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultBtc, "", "1000000", "140000000000000000", "affiliate_address", "10", "extra_memo_very_loooooooooooooong"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a7e3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a616666696c696174655f616464726573733a31303a65787472615f6d656d6f5f766572795f6c6f6f6f6f6f6f6f6f6f6f6f6f6f6f6e67"); + + auto tx = Bitcoin::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.amount(), 1000000); + EXPECT_EQ(tx.to_address(), VaultBtc); + EXPECT_EQ(tx.change_address(), Address1Btc); + EXPECT_EQ(tx.output_op_return(), "=:ETH.ETH:0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7:140000000000000000:affiliate_address:10:extra_memo_very_loooooooooooooong"); + EXPECT_EQ(tx.output_op_return().length(), 126ul); + EXPECT_EQ(tx.coin_type(), 0ul); + EXPECT_EQ(tx.private_key_size(), 0); + EXPECT_FALSE(tx.has_plan()); + + // set few fields before signing + tx.set_byte_fee(20); + EXPECT_EQ(Bitcoin::SegwitAddress(PrivateKey(TestKey1Btc).getPublicKey(TWPublicKeyTypeSECP256k1), "bc").string(), Address1Btc); + tx.add_private_key(TestKey1Btc.data(), TestKey1Btc.size()); + auto& utxo = *tx.add_utxo(); + Data utxoHash = parse_hex("1234000000000000000000000000000000000000000000000000000000005678"); + utxo.mutable_out_point()->set_hash(utxoHash.data(), utxoHash.size()); + utxo.mutable_out_point()->set_index(0); + utxo.mutable_out_point()->set_sequence(UINT32_MAX); + auto utxoScript = Bitcoin::Script::lockScriptForAddress(Address1Btc, TWCoinTypeBitcoin); + utxo.set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo.set_amount(50000000); + tx.set_use_max_amount(false); + + // sign and encode resulting input + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_memo); + EXPECT_EQ(hex(output.encoded()), ""); +} + TEST(THORChainSwap, Memo) { EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "", std::nullopt, ""), "SWAP:BTC.BTC:btc123:1234"); EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "", "bnb123", 1234, "", std::nullopt, ""), "SWAP:BNB.BNB:bnb123:1234"); From 984fed1b212123b760c283d5f7181516310ccf83 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 18 Nov 2022 14:53:17 +0100 Subject: [PATCH 045/426] feat(ios): update Package.swift (#2733) --- Package.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 7f6216bde07..7448baba099 100644 --- a/Package.swift +++ b/Package.swift @@ -12,13 +12,13 @@ let package = Package( targets: [ .binaryTarget( name: "WalletCore", - url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.6/WalletCore.xcframework.zip", - checksum: "a3df0c2b30fc59ede0a2600266fc19b8c0cf655dbef3fb832488c8ddedcb6b93" + url: "https://github.com/trustwallet/wallet-core/releases/download/3.1.0/WalletCore.xcframework.zip", + checksum: "2487af30a8f6f4775a41f29456f792f15269f4f4daae2da7e4b9ef8747613e3a" ), .binaryTarget( name: "SwiftProtobuf", - url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.6/SwiftProtobuf.xcframework.zip", - checksum: "61fa8483d4bd43f1898db6997eff0279426f15f9e518e12db0d762ec5f927a9b" + url: "https://github.com/trustwallet/wallet-core/releases/download/3.1.0/SwiftProtobuf.xcframework.zip", + checksum: "2038b1d43c9f6aeb4957e3763382b8558983a1a811b168dca71be3636bb8350a" ) ] ) From e3398f63c2970426ef56b4bb8ba226a634ccfa14 Mon Sep 17 00:00:00 2001 From: Uri Moszkowicz Date: Fri, 18 Nov 2022 08:12:24 -0600 Subject: [PATCH 046/426] [Breaking Change] Add Ripple token and NFT support (#2696) * Add Ripple token and NFT support --- .../ripple/TestRippleTransactionSigner.kt | 19 +- src/XRP/BinaryCoding.h | 22 ++- src/XRP/Signer.cpp | 100 ++++++++-- src/XRP/Signer.h | 7 + src/XRP/Transaction.cpp | 158 +++++++++++++++- src/XRP/Transaction.h | 151 ++++++++++++--- src/proto/Ripple.proto | 97 ++++++++-- swift/Tests/Blockchains/RippleTests.swift | 34 ++-- tests/chains/XRP/TWAnySignerTests.cpp | 176 +++++++++++++++++- tests/chains/XRP/TransactionTests.cpp | 30 ++- 10 files changed, 690 insertions(+), 104 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ripple/TestRippleTransactionSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ripple/TestRippleTransactionSigner.kt index d98aab2490d..9ec91772b1b 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ripple/TestRippleTransactionSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ripple/TestRippleTransactionSigner.kt @@ -19,20 +19,25 @@ class TestRippleTransactionSigner { @Test fun testRippleTransactionSigning() { + val operation = Ripple.OperationPayment.newBuilder() + operation.apply { + amount = 10 + destination = "rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF" + } val signingInput = Ripple.SigningInput.newBuilder() signingInput.apply { - account = "rDpysuumkweqeC7XdNgYNtzL5GxbdsmrtF" - amount = 29_000_000 - destination = "rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF" - fee = 200_000 - sequence = 1 - privateKey = ByteString.copyFrom(PrivateKey("ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764".toHexByteArray()).data()) + account = "rfxdLwsZnoespnTDDb1Xhvbc8EFNdztaoq" + fee = 10 + sequence = 32268248 + lastLedgerSequence = 32268269 + privateKey = ByteString.copyFrom(PrivateKey("a5576c0f63da10e584568c8d134569ff44017b0a249eb70657127ae04f38cc77".toHexByteArray()).data()) + opPayment = operation.build() } val sign = AnySigner.sign(signingInput.build(), XRP, SigningOutput.parser()) val signBytes = sign.encoded.toByteArray() - assertEquals(signBytes.toHex(), "0x12000022800000002400000001614000000001ba8140684000000000030d407321026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6744630440220067f20b3eebfc7107dd0bcc72337a236ac3be042c0469f2341d76694a17d4bb9022048393d7ee7dcb729783b33f5038939ddce1bb8337e66d752974626854556bbb681148400b6b6d08d5d495653d73eda6804c249a5148883148132e4e20aecf29090ac428a9c43f230a829220d") + assertEquals(signBytes.toHex(), "0x12000022000000002401ec5fd8201b01ec5fed61400000000000000a68400000000000000a732103d13e1152965a51a4a9fd9a8b4ea3dd82a4eba6b25fcad5f460a2342bb650333f74463044022037d32835c9394f39b2cfd4eaf5b0a80e0db397ace06630fa2b099ff73e425dbc02205288f780330b7a88a1980fa83c647b5908502ad7de9a44500c08f0750b0d9e8481144c55f5a78067206507580be7bb2686c8460adff983148132e4e20aecf29090ac428a9c43f230a829220d") } } diff --git a/src/XRP/BinaryCoding.h b/src/XRP/BinaryCoding.h index d606116d6c2..5f88d79d739 100644 --- a/src/XRP/BinaryCoding.h +++ b/src/XRP/BinaryCoding.h @@ -9,6 +9,7 @@ #include #include #include +#include "Data.h" namespace TW::Ripple { @@ -17,11 +18,20 @@ enum class FieldType; /// Encodes a field type. inline void encodeType(FieldType type, int key, std::vector& data) { const auto typeValue = static_cast(type); - if (key <= 0xf) { - data.push_back(static_cast((typeValue << 4) | key)); + if (typeValue <= 0xf) { + if (key <= 0xf) { + data.emplace_back(static_cast((typeValue << 4) | key)); + } else { + data.emplace_back(static_cast(typeValue << 4)); + data.emplace_back(static_cast(key)); + } + } else if (key <= 0xf) { + data.emplace_back(static_cast(key)); + data.emplace_back(static_cast(typeValue)); } else { - data.push_back(static_cast(typeValue << 4)); - data.push_back(static_cast(key)); + data.emplace_back(0); + data.emplace_back(static_cast(typeValue)); + data.emplace_back(static_cast(key)); } } @@ -47,4 +57,8 @@ inline void encodeBytes(std::vector bytes, std::vector& data) data.insert(data.end(), bytes.begin(), bytes.end()); } +inline void encodeZeros(std::size_t len, std::vector& data) { + append(data, Data(len)); +} + } // namespace TW::Ripple diff --git a/src/XRP/Signer.cpp b/src/XRP/Signer.cpp index 52237c2fdba..f72a257e9bd 100644 --- a/src/XRP/Signer.cpp +++ b/src/XRP/Signer.cpp @@ -10,39 +10,67 @@ namespace TW::Ripple { -static const int64_t fullyCanonical = 0x80000000; Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto output = Proto::SigningOutput(); - const int64_t tag = input.destination_tag(); - if (tag > std::numeric_limits::max() || tag < 0) { - output.set_error(Common::Proto::SigningError::Error_invalid_memo); - return output; + auto transaction = + Transaction( + input.fee(), + input.flags(), + input.sequence(), + input.last_ledger_sequence(), + Address(input.account())); + switch (input.operation_oneof_case()) { + case Proto::SigningInput::kOpPayment: + signPayment(input, output, transaction); + break; + + case Proto::SigningInput::kOpNftokenBurn: + transaction.createNFTokenBurn(input.op_nftoken_burn().nftoken_id()); + break; + + case Proto::SigningInput::kOpNftokenCreateOffer: + transaction.createNFTokenCreateOffer( + input.op_nftoken_create_offer().nftoken_id(), + input.op_nftoken_create_offer().destination()); + break; + + case Proto::SigningInput::kOpNftokenAcceptOffer: + transaction.createNFTokenAcceptOffer(input.op_nftoken_accept_offer().sell_offer()); + break; + + case Proto::SigningInput::kOpNftokenCancelOffer: + signNfTokenCancelOffer(input, transaction); + break; + + case Proto::SigningInput::kOpTrustSet: + transaction.createTrustSet( + input.op_trust_set().limit_amount().currency(), + input.op_trust_set().limit_amount().value(), + input.op_trust_set().limit_amount().issuer()); + break; + + default: + break; } - auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto transaction = Transaction( - /* amount */ input.amount(), - /* fee */ input.fee(), - /* flags */ input.flags(), - /* sequence */ input.sequence(), - /* last_ledger_sequence */ input.last_ledger_sequence(), - /* account */ Address(input.account()), - /* destination */ input.destination(), - /* destination_tag*/ tag); + if (output.error()) { + return output; + } auto signer = Signer(); + auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); signer.sign(key, transaction); auto encoded = transaction.serialize(); output.set_encoded(encoded.data(), encoded.size()); + return output; } void Signer::sign(const PrivateKey& privateKey, Transaction& transaction) const noexcept { /// See https://github.com/trezor/trezor-core/blob/master/src/apps/ripple/sign_tx.py#L59 - transaction.flags |= fullyCanonical; transaction.pub_key = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1).bytes; auto unsignedTx = transaction.getPreImage(); @@ -52,4 +80,44 @@ void Signer::sign(const PrivateKey& privateKey, Transaction& transaction) const transaction.signature = privateKey.signAsDER(half); } +void Signer::signPayment(const Proto::SigningInput& input, + Proto::SigningOutput& output, + Transaction& transaction) noexcept { + const int64_t tag = input.op_payment().destination_tag(); + if (tag > std::numeric_limits::max() || tag < 0) { + output.set_error(Common::Proto::SigningError::Error_invalid_memo); + return; + } + + switch (input.op_payment().amount_oneof_case()) { + case Proto::OperationPayment::kAmount: + transaction.createXrpPayment( + input.op_payment().amount(), + input.op_payment().destination(), + tag); + break; + + case Proto::OperationPayment::kCurrencyAmount: + transaction.createTokenPayment( + input.op_payment().currency_amount().currency(), + input.op_payment().currency_amount().value(), + input.op_payment().currency_amount().issuer(), + input.op_payment().destination(), + tag); + break; + + default: + break; + } +} + +void Signer::signNfTokenCancelOffer(const Proto::SigningInput& input, Transaction& transaction) noexcept { + std::vector token_offers; + for (int i = 0; i < input.op_nftoken_cancel_offer().token_offers_size(); i++) { + token_offers.emplace_back(input.op_nftoken_cancel_offer().token_offers(i)); + } + + transaction.createNFTokenCancelOffer(token_offers); +} + } // namespace TW::Ripple diff --git a/src/XRP/Signer.h b/src/XRP/Signer.h index ab49851839b..2225e751ce2 100644 --- a/src/XRP/Signer.h +++ b/src/XRP/Signer.h @@ -21,6 +21,13 @@ class Signer { /// Signs the given transaction. void sign(const PrivateKey& privateKey, Transaction& transaction) const noexcept; + + private: + static void signPayment(const Proto::SigningInput& input, + Proto::SigningOutput& output, + Transaction& transaction) noexcept; + + static void signNfTokenCancelOffer(const Proto::SigningInput& input, Transaction& transaction) noexcept; }; } // namespace TW::Ripple diff --git a/src/XRP/Transaction.cpp b/src/XRP/Transaction.cpp index 00846a905c7..25b6625aaef 100644 --- a/src/XRP/Transaction.cpp +++ b/src/XRP/Transaction.cpp @@ -9,55 +9,103 @@ #include "../BinaryCoding.h" #include +#include +#include namespace TW::Ripple { const int NETWORK_PREFIX = 0x53545800; Data Transaction::serialize() const { + // See https://xrpl.org/serialization.html + auto data = Data(); + /// field must be sorted by field type then by field name /// "type" encodeType(FieldType::int16, 2, data); - encode16BE(uint16_t(TransactionType::payment), data); + encode16BE(uint16_t(transaction_type), data); + /// "flags" encodeType(FieldType::int32, 2, data); encode32BE(static_cast(flags), data); + /// "sequence" encodeType(FieldType::int32, 4, data); encode32BE(sequence, data); + /// "destinationTag" - if (encode_tag) { + if ((transaction_type == TransactionType::payment) && encode_tag) { encodeType(FieldType::int32, 14, data); encode32BE(static_cast(destination_tag), data); } + /// "lastLedgerSequence" if (last_ledger_sequence > 0) { encodeType(FieldType::int32, 27, data); encode32BE(last_ledger_sequence, data); } + + /// "NFTokenId" + if ((transaction_type == TransactionType::NFTokenCreateOffer) || + (transaction_type == TransactionType::NFTokenBurn)) { + encodeType(FieldType::hash256, 10, data); + data.insert(data.end(), nftoken_id.begin(), nftoken_id.end()); + } + + /// "NFTokenAcceptOffer" + if (transaction_type == TransactionType::NFTokenAcceptOffer) { + encodeType(FieldType::hash256, 29, data); + data.insert(data.end(), sell_offer.begin(), sell_offer.end()); + } + /// "amount" - encodeType(FieldType::amount, 1, data); - append(data, serializeAmount(amount)); + if ((transaction_type == TransactionType::payment) || + (transaction_type == TransactionType::NFTokenCreateOffer)) { + encodeType(FieldType::amount, 1, data); + append(data, + (currency_amount.currency.size() > 0) ? + serializeCurrencyAmount(currency_amount) : + serializeAmount(amount)); + } else if (transaction_type == TransactionType::TrustSet) { + encodeType(FieldType::amount, 3, data); + append(data, serializeCurrencyAmount(limit_amount)); + } + /// "fee" encodeType(FieldType::amount, 8, data); append(data, serializeAmount(fee)); + /// "signingPubKey" if (!pub_key.empty()) { encodeType(FieldType::vl, 3, data); encodeBytes(pub_key, data); } + /// "txnSignature" if (!signature.empty()) { encodeType(FieldType::vl, 4, data); encodeBytes(signature, data); } + /// "account" encodeType(FieldType::account, 1, data); encodeBytes(serializeAddress(account), data); + /// "destination" - encodeType(FieldType::account, 3, data); - encodeBytes(destination, data); + if ((transaction_type == TransactionType::payment) || + (transaction_type == TransactionType::NFTokenCreateOffer)) { + encodeType(FieldType::account, 3, data); + encodeBytes(destination, data); + } + + /// "NFTokenOffers" + if (transaction_type == TransactionType::NFTokenCancelOffer) { + // only support one offer + encodeType(FieldType::vector256, 4, data); + encodeBytes(token_offers, data); + } + return data; } @@ -81,6 +129,104 @@ Data Transaction::serializeAmount(int64_t amount) { return data; } +Data Transaction::serializeCurrencyAmount(const CurrencyAmount& currency_amount) { + // Calculate value + // https://xrpl.org/serialization.html#token-amount-format + int64_t sign = 0; + int64_t mantissa = 0; + int32_t exp = 0; + try { + int32_t num_after_dot = 0; + bool after_dot = false; + bool after_e = false; + bool has_exp = false; + std::ostringstream mantissa_oss, exp_oss; + for (auto i : currency_amount.value) { + if (i == '.') { + after_dot = true; + } else if (i == 'e') { + after_dot = false; + after_e = true; + } else if (after_e) { + has_exp = true; + exp_oss << i; + } else { + mantissa_oss << i; + if (after_dot) { + num_after_dot++; + } + } + } + + mantissa = std::stoll(mantissa_oss.str()); + sign = (mantissa >= 0) ? 1 : 0; + mantissa = (mantissa < 0) ? (mantissa * -1) : mantissa; + + exp = has_exp ? std::stoi(exp_oss.str()) : 0; + exp -= num_after_dot; + } catch (const std::exception& e) { + return Data(); + } + + int64_t min_mantissa = (uint64_t)pow(10, 15); + int64_t max_mantissa = (uint64_t)pow(10, 16) - 1; + int32_t min_exp = -96; + int32_t max_exp = 80; + + while ((mantissa < min_mantissa) && (exp > min_exp)) { + mantissa *= 10; + exp--; + } + + while (mantissa > max_mantissa) { + if (exp >= max_exp) { + return Data(); + } + + mantissa /= 10; + exp++; + } + + if (((exp < min_exp) || (mantissa < min_mantissa)) || + ((exp > max_exp) || (mantissa > max_mantissa))) { + return Data(); + } + + typedef union { + uint64_t value; + struct { + uint64_t mantissa : 54; + uint64_t exp : 8; + uint64_t sign : 1; + uint64_t not_xrp : 1; + } parts; + } AmountCast; + + AmountCast amount_cast; + amount_cast.parts.mantissa = mantissa; + amount_cast.parts.exp = exp + 97; + amount_cast.parts.sign = sign; + amount_cast.parts.not_xrp = 1; + + // Serialize fields + // https://xrpl.org/serialization.html#amount-fields + auto data = Data(); + encode64BE(amount_cast.value, data); + + // ISO-4217 currency code + encodeZeros(1, data); // type code (0x00) + encodeZeros(11, data); // reserved + if (currency_amount.currency.size() == 3) { + data.insert(data.end(), currency_amount.currency.begin(), currency_amount.currency.end()); + } else { + encodeZeros(3, data); // none + } + + encodeZeros(5, data); // reserved + data.insert(data.end(), currency_amount.issuer.begin(), currency_amount.issuer.end()); + return data; +} + Data Transaction::serializeAddress(Address address) { auto data = Data(20); std::copy(&address.bytes[0] + 1, &address.bytes[0] + std::min(address.bytes.size(), size_t(21)), &data[0]); diff --git a/src/XRP/Transaction.h b/src/XRP/Transaction.h index a51f82eefab..836fcc5a4a5 100644 --- a/src/XRP/Transaction.h +++ b/src/XRP/Transaction.h @@ -10,25 +10,50 @@ #include "XAddress.h" #include "Data.h" #include "../proto/Ripple.pb.h" +#include "../HexCoding.h" namespace TW::Ripple { +// See https://github.com/XRPLF/rippled/blob/master/src/ripple/protocol/SField.h#L57-L74 enum class FieldType: int { - int16 = 1, - int32 = 2, - amount = 6, - vl = 7, - account = 8 + int16 = 1, + int32 = 2, + hash256 = 5, + amount = 6, + vl = 7, + account = 8, + vector256 = 19 }; -enum class TransactionType { payment = 0 }; +// See https://github.com/XRPLF/xrpl.js/blob/main/packages/ripple-binary-codec/src/enums/definitions.json +enum class TransactionType { + no_type = -1, + payment = 0, + TrustSet = 20, + NFTokenBurn = 26, + NFTokenCreateOffer = 27, + NFTokenCancelOffer = 28, + NFTokenAcceptOffer = 29 +}; + +// See https://xrpl.org/nftokencreateoffer.html +enum class NFTokenCreateOfferFlags: int64_t { + tfSellNFToken = 0x00000001 +}; class Transaction { - /// We only support transaction types other than the Payment transaction. - /// Non-XRP currencies are not supported. Float and negative amounts are not supported. + /// Float and negative amounts are not supported. /// See https://github.com/trezor/trezor-core/tree/master/src/apps/ripple#transactions public: + struct CurrencyAmount { + Data currency; + Data value; + Data issuer; + }; + int64_t amount; + CurrencyAmount currency_amount; + CurrencyAmount limit_amount; int64_t fee; int64_t flags; int32_t sequence; @@ -39,28 +64,74 @@ class Transaction { int64_t destination_tag; Data pub_key; Data signature; + Data nftoken_id; + Data sell_offer; + Data token_offers; + TransactionType transaction_type; - Transaction(int64_t amount, int64_t fee, int64_t flags, int32_t sequence, - int32_t last_ledger_sequence, Address account, const std::string& destination, - int64_t destination_tag) - : amount(amount) + Transaction(int64_t fee, int64_t flags, int32_t sequence, int32_t last_ledger_sequence, Address p_account) + : amount(0) , fee(fee) , flags(flags) , sequence(sequence) , last_ledger_sequence(last_ledger_sequence) - , 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()); - } + , account(p_account) + , encode_tag(false) + , destination_tag(0) + , nftoken_id(0) + , sell_offer(0) + , token_offers(0) + , transaction_type(TransactionType::no_type) {} + + void createXrpPayment(int64_t p_amount, const std::string& p_destination, int64_t p_destination_tag) { + transaction_type = TransactionType::payment; + amount = p_amount; + setDestination(p_destination, p_destination_tag); + } + + void createTrustSet(const std::string& currency, const std::string& issuer) { + // Use maximum amount + // https://xrpl.org/currency-formats.html + std::string value("9999999999999999e80"); + createTrustSet(currency, value, issuer); + } + + void createTrustSet(const std::string& currency, const std::string& value, const std::string& issuer) { + transaction_type = TransactionType::TrustSet; + setCurrencyAmount(limit_amount, currency, value, issuer); + } + + void createTokenPayment(const std::string& currency, const std::string& value, const std::string& issuer, + const std::string& p_destination, int64_t p_destination_tag) { + transaction_type = TransactionType::payment; + setDestination(p_destination, p_destination_tag); + setCurrencyAmount(currency_amount, currency, value, issuer); + } + + void createNFTokenBurn(const std::string& p_nftoken_id) { + transaction_type = TransactionType::NFTokenBurn; + nftoken_id = parse_hex(p_nftoken_id); + } + + void createNFTokenCreateOffer(const std::string& p_nftoken_id, const std::string& p_destination) { + transaction_type = TransactionType::NFTokenCreateOffer; + flags = int64_t(NFTokenCreateOfferFlags::tfSellNFToken); + nftoken_id = parse_hex(p_nftoken_id); + setAccount(p_destination, destination); + } + + void createNFTokenAcceptOffer(const std::string& p_sell_offer) { + transaction_type = TransactionType::NFTokenAcceptOffer; + sell_offer = parse_hex(p_sell_offer); + } + + void createNFTokenCancelOffer(const std::vector p_token_offers) { + transaction_type = TransactionType::NFTokenCancelOffer; + for (auto i : p_token_offers) { + Data token_offer = parse_hex(i); + token_offers.insert(token_offers.end(), token_offer.begin(), token_offer.end()); } + } public: /// simplified serialization format tailored for Payment transaction type @@ -69,7 +140,39 @@ class Transaction { Data getPreImage() const; static Data serializeAmount(int64_t amount); + static Data serializeCurrencyAmount(const CurrencyAmount& currency_amount); static Data serializeAddress(Address address); + + private: + void setCurrencyAmount(CurrencyAmount& p_currency_amount, const std::string& currency, const std::string& value, const std::string& issuer) { + p_currency_amount.currency = Data(currency.begin(), currency.end()); + p_currency_amount.value = Data(value.begin(), value.end()); + setAccount(issuer, p_currency_amount.issuer); + } + + void setDestination(const std::string& p_destination, int64_t p_destination_tag) { + try { + auto address = Address(p_destination); + encode_tag = p_destination_tag > 0; + destination_tag = p_destination_tag; + destination = Data(address.bytes.begin() + 1, address.bytes.end()); + } catch(const std::exception& e) { + auto xAddress = XAddress(p_destination); + encode_tag = xAddress.flag != TagFlag::none; + destination_tag = xAddress.tag; + destination = Data(xAddress.bytes.begin(), xAddress.bytes.end()); + } + } + + void setAccount(const std::string& p_account, Data& data) { + try { + auto address = Address(p_account); + data = Data(address.bytes.begin() + 1, address.bytes.end()); + } catch(const std::exception& e) { + auto xAddress = XAddress(p_account); + data = Data(xAddress.bytes.begin(), xAddress.bytes.end()); + } + } }; } // namespace TW::Ripple diff --git a/src/proto/Ripple.proto b/src/proto/Ripple.proto index ffbb863741c..dd903dc7435 100644 --- a/src/proto/Ripple.proto +++ b/src/proto/Ripple.proto @@ -5,34 +5,101 @@ option java_package = "wallet.core.jni.proto"; import "Common.proto"; -// Input data necessary to create a signed transaction. -message SigningInput { +// https://xrpl.org/currency-formats.html#token-amounts +message CurrencyAmount { + // Currency code + // https://xrpl.org/currency-formats.html#currency-codes + string currency = 1; + + // String number + // https://xrpl.org/currency-formats.html#string-numbers + string value = 2; + + // Account + // https://xrpl.org/accounts.html + string issuer = 3; +} + +// https://xrpl.org/trustset.html +message OperationTrustSet { + CurrencyAmount limit_amount = 1; +} + +// https://xrpl.org/payment.html +message OperationPayment { // Transfer amount - int64 amount = 1; + oneof amount_oneof { + int64 amount = 1; + CurrencyAmount currency_amount = 2; + } + + // Target account + string destination = 3; + + // A Destination Tag + int64 destination_tag = 4; +} + +// https://xrpl.org/nftokenburn.html +message OperationNFTokenBurn { + // Hash256 NFTokenId + bytes nftoken_id = 1; +} + +// https://xrpl.org/nftokencreateoffer.html +message OperationNFTokenCreateOffer { + // Hash256 NFTokenId + bytes nftoken_id = 1; + + // Destination account + string destination = 2; +} + +// https://xrpl.org/nftokenacceptoffer.html +message OperationNFTokenAcceptOffer { + // Hash256 NFTokenOffer + bytes sell_offer = 1; +} + +// https://xrpl.org/nftokencanceloffer.html +message OperationNFTokenCancelOffer { + // Vector256 NFTokenOffers + repeated bytes token_offers = 1; +} +// Input data necessary to create a signed transaction. +message SigningInput { // Transfer fee - int64 fee = 2; + int64 fee = 1; // Account sequence number - int32 sequence = 3; + int32 sequence = 2; // Ledger sequence number - int32 last_ledger_sequence = 4; + int32 last_ledger_sequence = 3; // Source account - string account = 5; - - // Target account - string destination = 6; - - // A Destination Tag - int64 destination_tag = 7; + string account = 4; // Transaction flags, optional - int64 flags = 8; + int64 flags = 5; // The secret private key used for signing (32 bytes). - bytes private_key = 9; + bytes private_key = 6; + + oneof operation_oneof { + OperationTrustSet op_trust_set = 7; + + OperationPayment op_payment = 8; + + OperationNFTokenBurn op_nftoken_burn = 9; + + OperationNFTokenCreateOffer op_nftoken_create_offer = 10; + + OperationNFTokenAcceptOffer op_nftoken_accept_offer = 11; + + OperationNFTokenCancelOffer op_nftoken_cancel_offer = 12; + } } // Result containing the signed and encoded transaction. diff --git a/swift/Tests/Blockchains/RippleTests.swift b/swift/Tests/Blockchains/RippleTests.swift index e42151c04e3..c2e96a65781 100644 --- a/swift/Tests/Blockchains/RippleTests.swift +++ b/swift/Tests/Blockchains/RippleTests.swift @@ -29,25 +29,33 @@ class RippleTests: XCTestCase { } func testSigner() { - let input = RippleSigningInput.with { - $0.amount = 29_000_000 - $0.fee = 200_000 - $0.sequence = 1 // from account info api - $0.account = "rDpysuumkweqeC7XdNgYNtzL5GxbdsmrtF" + let operation = RippleOperationPayment.with { $0.destination = "rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF" - $0.privateKey = Data(hexString: "ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764")! + $0.amount = 10 + } + let input = RippleSigningInput.with { + $0.fee = 10 + $0.sequence = 32268248 // from account info api + $0.lastLedgerSequence = 32268269 + $0.account = "rfxdLwsZnoespnTDDb1Xhvbc8EFNdztaoq" + $0.privateKey = Data(hexString: "a5576c0f63da10e584568c8d134569ff44017b0a249eb70657127ae04f38cc77")! + $0.opPayment = operation } let output: RippleSigningOutput = AnySigner.sign(input: input, coin: .xrp) - XCTAssertEqual(output.encoded.hexString, "12000022800000002400000001614000000001ba8140684000000000030d407321026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6744630440220067f20b3eebfc7107dd0bcc72337a236ac3be042c0469f2341d76694a17d4bb9022048393d7ee7dcb729783b33f5038939ddce1bb8337e66d752974626854556bbb681148400b6b6d08d5d495653d73eda6804c249a5148883148132e4e20aecf29090ac428a9c43f230a829220d") + XCTAssertEqual(output.encoded.hexString, "12000022000000002401ec5fd8201b01ec5fed61400000000000000a68400000000000000a732103d13e1152965a51a4a9fd9a8b4ea3dd82a4eba6b25fcad5f460a2342bb650333f74463044022037d32835c9394f39b2cfd4eaf5b0a80e0db397ace06630fa2b099ff73e425dbc02205288f780330b7a88a1980fa83c647b5908502ad7de9a44500c08f0750b0d9e8481144c55f5a78067206507580be7bb2686c8460adff983148132e4e20aecf29090ac428a9c43f230a829220d") - let input2 = RippleSigningInput.with { - $0.amount = 29_000_000 - $0.fee = 200_000 - $0.sequence = 1 // from account info api - $0.account = "rDpysuumkweqeC7XdNgYNtzL5GxbdsmrtF" + let operation2 = RippleOperationPayment.with { $0.destination = "XVfvixWZQKkcenFRYApCjpTUyJ4BePMjMaPqnob9QVPiVJV" - $0.privateKey = Data(hexString: "ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764")! + $0.amount = 10 + } + let input2 = RippleSigningInput.with { + $0.fee = 10 + $0.sequence = 32268248 // from account info api + $0.lastLedgerSequence = 32268269 + $0.account = "rfxdLwsZnoespnTDDb1Xhvbc8EFNdztaoq" + $0.privateKey = Data(hexString: "a5576c0f63da10e584568c8d134569ff44017b0a249eb70657127ae04f38cc77")! + $0.opPayment = operation } let output2: RippleSigningOutput = AnySigner.sign(input: input2, coin: .xrp) XCTAssertEqual(output2.encoded, output.encoded) diff --git a/tests/chains/XRP/TWAnySignerTests.cpp b/tests/chains/XRP/TWAnySignerTests.cpp index 1b9e05728e6..47fcbd9d232 100644 --- a/tests/chains/XRP/TWAnySignerTests.cpp +++ b/tests/chains/XRP/TWAnySignerTests.cpp @@ -16,24 +16,26 @@ using namespace TW; namespace TW::Ripple::tests { -TEST(TWAnySignerRipple, Sign) { - auto key = parse_hex("ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764"); +TEST(TWAnySignerRipple, SignXrpPayment) { + // https://testnet.xrpl.org/transactions/A202034796F37F38D1D20F2025DECECB1623FC801F041FC694199C0D0E49A739 + auto key = parse_hex("a5576c0f63da10e584568c8d134569ff44017b0a249eb70657127ae04f38cc77"); Proto::SigningInput input; - input.set_amount(29000000); - input.set_fee(200000); - input.set_sequence(1); - input.set_account("rDpysuumkweqeC7XdNgYNtzL5GxbdsmrtF"); - input.set_destination("rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"); + input.mutable_op_payment()->set_amount(10); + input.set_fee(10); + input.set_sequence(32268248); + input.set_last_ledger_sequence(32268269); + input.set_account("rfxdLwsZnoespnTDDb1Xhvbc8EFNdztaoq"); + input.mutable_op_payment()->set_destination("rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"); input.set_private_key(key.data(), key.size()); Proto::SigningOutput output; ANY_SIGN(input, TWCoinTypeXRP); - EXPECT_EQ(hex(output.encoded()), "12000022800000002400000001614000000001ba8140684000000000030d407321026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6744630440220067f20b3eebfc7107dd0bcc72337a236ac3be042c0469f2341d76694a17d4bb9022048393d7ee7dcb729783b33f5038939ddce1bb8337e66d752974626854556bbb681148400b6b6d08d5d495653d73eda6804c249a5148883148132e4e20aecf29090ac428a9c43f230a829220d"); + EXPECT_EQ(hex(output.encoded()), "12000022000000002401ec5fd8201b01ec5fed61400000000000000a68400000000000000a732103d13e1152965a51a4a9fd9a8b4ea3dd82a4eba6b25fcad5f460a2342bb650333f74463044022037d32835c9394f39b2cfd4eaf5b0a80e0db397ace06630fa2b099ff73e425dbc02205288f780330b7a88a1980fa83c647b5908502ad7de9a44500c08f0750b0d9e8481144c55f5a78067206507580be7bb2686c8460adff983148132e4e20aecf29090ac428a9c43f230a829220d"); // invalid tag - input.set_destination_tag(641505641505); + input.mutable_op_payment()->set_destination_tag(641505641505); ANY_SIGN(input, TWCoinTypeXRP); @@ -41,4 +43,160 @@ TEST(TWAnySignerRipple, Sign) { EXPECT_EQ(output.encoded(), ""); } +TEST(TWAnySignerRipple, SignXrpPaymentMain) { + // https://xrpscan.com/tx/4B9D022E8C77D798B7D11C41FDFDCF468F03A5564151C520EECA1E96FF1A1610 + auto key = parse_hex("acf1bbf6264e699da0cc65d17ac03fcca6ded1522d19529df7762db46097ff9f"); + Proto::SigningInput input; + + input.mutable_op_payment()->set_amount(1000000); + input.set_fee(10); + input.set_sequence(75674534); + input.set_last_ledger_sequence(75674797); + input.set_account("rGV1v1xw23PHcRn4Km4tF8R2mfh6yTZkcP"); + input.mutable_op_payment()->set_destination("rNLpgsBTCwiaZAnHe2ZViAN1GcXZtYW6rg"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "1200002200000000240482b3a6201b0482b4ad6140000000000f424068400000000000000a7321027efc5f15071d2ae5e73ee09a0c17456c5d9170a41d67e3297c554829199be80b74473045022100e1c746c3aeebc8278c627ee4c2ce5cae97e3856292c7fe5388f803920230a37b02207d2eccb76cd35dd379d6b24c2cabd786e62d34a564cf083e863176109c5b6bb48114aa000c09c692ef1f82787e51e22833149941ea2083149232ef60695add51f0f84534cc4084e4fdfc698e"); +} + +TEST(TWAnySignerRipple, SignTrustSetPayment) { + // https://testnet.xrpl.org/transactions/31042345374CFF785B3F7E2A3716E3BAB7E2CAA30D40F5E488E67ABA116655B9 + auto key = parse_hex("8753e78ee2963f301f82e5eeab2754f593fc242ce94273dd2fb0684e3b0f2b91"); + Proto::SigningInput input; + + input.mutable_op_trust_set()->mutable_limit_amount()->set_currency("USD"); + input.mutable_op_trust_set()->mutable_limit_amount()->set_value("10"); + input.mutable_op_trust_set()->mutable_limit_amount()->set_issuer("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"); + input.set_fee(10); + input.set_sequence(32268473); + input.set_last_ledger_sequence(32268494); + input.set_account("rnRkLPni2Q5yMxSqyJSJEkKUfQNFkaAspS"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12001422000000002401ec60b9201b01ec60ce63d4c38d7ea4c6800000000000000000000000000055534400000000004b4e9c06f24296074f7bc48f92a97916c6dc5ea968400000000000000a732103dc4a0dae2d550de7cace9c26c1a331a114e3e7efee5577204b476d27e2dc683a7446304402206ebcc7a689845df373dd2566cd3789862d426d9ad4e6a09c2d2772b57e82696a022066b1f217a0f0d834d167613a313f74097423a9ccd11f1ae7f90ffab0d2fc26b58114308ea8e515b64f2e6616a33b42e1bbb9fa00bbd2"); +} + +TEST(TWAnySignerRipple, SignTokenPayment0) { + // https://testnet.xrpl.org/transactions/8F7820892294598B58CFA2E1101D15ED98C179B25A2BA6DAEB4F5B727CB00D4E + for (auto value : std::vector({"10", "10e0", "10.0", "10.0e0", "1e1", ".1e2", "0.1e2", "100e-1", "10000000000000000e-15", "0.0000000000000001e17"})) { + auto key = parse_hex("4ba5fd2ebf0f5d7e579b3c354c263ebb39cda4093845125786a280301af14e21"); + Proto::SigningInput input; + + input.mutable_op_payment()->mutable_currency_amount()->set_currency("USD"); + input.mutable_op_payment()->mutable_currency_amount()->set_value("10"); + input.mutable_op_payment()->mutable_currency_amount()->set_issuer("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"); + input.set_fee(10); + input.set_sequence(32268645); + input.set_last_ledger_sequence(32268666); + input.set_account("raPAA61ca99bdwNiZs5JJukR5rvkHWvkBX"); + input.mutable_op_payment()->set_destination("rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12000022000000002401ec6165201b01ec617a61d4c38d7ea4c6800000000000000000000000000055534400000000004b4e9c06f24296074f7bc48f92a97916c6dc5ea968400000000000000a7321020652a477b0cca8b74d6e68a6a386a836b226101617481b95180eaffbe841b3227446304402203e925caeb05006afb135254e9ae4e46de2019db6c6f68614ef969885063a777602206af110fc29775256fcad8b14974c6a838141d82193192d3b57324fe1079afa1781143b2fa4f36553e5b7a4f54ff9e6883e44b4b0dbb383148132e4e20aecf29090ac428a9c43f230a829220d"); + } +} + +TEST(TWAnySignerRipple, SignTokenPayment1) { + // https://testnet.xrpl.org/transactions/14606DAAFA54DB29B738000DFC133312B341FFC1D22D57AE0C8D54C9C56E19D8 + auto key = parse_hex("4041882ce8c2ceea6f4cfe1a067b927c1e1eb2f5eb025eaf2f429479a7ec3738"); + Proto::SigningInput input; + + input.mutable_op_payment()->mutable_currency_amount()->set_currency("USD"); + input.mutable_op_payment()->mutable_currency_amount()->set_value("29.3e-1"); + input.mutable_op_payment()->mutable_currency_amount()->set_issuer("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"); + input.set_fee(10); + input.set_sequence(32268768); + input.set_last_ledger_sequence(32268789); + input.set_account("raJe5XVt99649qn5Pg7cKdmgEYdN3d4Mky"); + input.mutable_op_payment()->set_destination("rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12000022000000002401ec61e0201b01ec61f561d48a68d1c931200000000000000000000000000055534400000000004b4e9c06f24296074f7bc48f92a97916c6dc5ea968400000000000000a73210348c331ab218ba964150490c83875b06ccad2100b1f5707f296764712738cf1ca74473045022100a938783258d33e2e3e6099d1ab68fd85c3fd21adfa00e136a67bed8fddec6c9a02206cc6784c1f212f19dc939207643d361ceaa8334eb366722cf33b24dc7669dd7a81143a2f2f189d05abb8519cc9dee0e2dbc6fa53924183148132e4e20aecf29090ac428a9c43f230a829220d"); +} + +TEST(TWAnySignerRipple, SignNfTokenBurn) { + // https://devnet.xrpl.org/transactions/37DA90BE3C30016B3A2C3D47D9677278A3F6D4141B318793CE6AA467A6530E2D + auto key = parse_hex("7c2ea5c7b1fd7dfc62d879918b7fc779cdff6bf6391d02ec99854297e916318e"); + Proto::SigningInput input; + + input.mutable_op_nftoken_burn()->set_nftoken_id("000b013a95f14b0044f78a264e41713c64b5f89242540ee208c3098e00000d65"); + input.set_fee(10); + input.set_sequence(22858395); + input.set_last_ledger_sequence(22858416); + input.set_account("rhR1mTXkg4iSGhz7zsEKBLbV3MkopivPVF"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12001a220000000024015cca9b201b015ccab05a000b013a95f14b0044f78a264e41713c64b5f89242540ee208c3098e00000d6568400000000000000a73210254fc876043109af1ff11b832320be4436ef51dcc344da5970c9b6c6d1fbcddcf744730450221008b4d437bc92aa4643b275b17c0f88a1bef2c1c160ece5faf93b03e2d31b8278602207640e7e35426352deaafecf61e2b401a4ea1fc645839280370a72fa3c41aea7d8114259cbcf9635360bc302f27d0ce72c18d4dbe9c8d"); +} + +TEST(TWAnySignerRipple, SignNfTokenCreateOffer) { + // https://devnet.xrpl.org/transactions/E61D66E261DB89CEAAB4F54ECF792B329296CB524E8B40EA99D27CF5E16DD27D + auto key = parse_hex("1963884da4a4da79ad7681d106b2c55fb652c68ca7b288dd12bb86cd40b9d940"); + Proto::SigningInput input; + + input.mutable_op_nftoken_create_offer()->set_nftoken_id("000b013a95f14b0044f78a264e41713c64b5f89242540ee208c3098e00000d65"); + input.mutable_op_nftoken_create_offer()->set_destination("rDxTa8vhigDUCq9nmZY8jAkFne5XrcYbxG"); + input.set_fee(10); + input.set_sequence(22857522); + input.set_last_ledger_sequence(22857543); + input.set_account("rJdxtrVoL3Tak74EzN8SdMxFF6RP9smjJJ"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12001b220000000124015cc732201b015cc7475a000b013a95f14b0044f78a264e41713c64b5f89242540ee208c3098e00000d6561400000000000000068400000000000000a7321022707066e4f8b87b749ef802338be064065dc978f0ea52ea9c8c8ea0a6145571974473045022100a148140469b8e9e2f9aa43631f3101e532d161d49a05e739cd3494ea208bd657022029a9752df3fc0d23b8fdb46d2274e69ab198ce6f373aeb7cdd0d81ab05aff6f48114c177c23ed1f5d175f42fd7970ece74ac18d61c4d83148e1e2ca343165bf30e96abead961f7a34510ad93"); +} + +TEST(TWAnySignerRipple, SignNfTokenAcceptOffer) { + // https://devnet.xrpl.org/transactions/6BB00A7BABB8797D60E3AB0E52DB64562524D014833977D87B04CA9FA3F56AD7 + auto key = parse_hex("3c01b3458d2b2a4b86a5699d11682d791b5c3136692c5594f7a8ca7f3967e7ae"); + Proto::SigningInput input; + + input.mutable_op_nftoken_accept_offer()->set_sell_offer("000b013a95f14b0044f78a264e41713c64b5f89242540ee208c3098e00000d65"); + input.set_fee(10); + input.set_sequence(22857743); + input.set_last_ledger_sequence(22857764); + input.set_account("rPa2KsEuSuZnmjosds99nhgsoiKtw85j6Z"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12001d220000000024015cc80f201b015cc824501d000b013a95f14b0044f78a264e41713c64b5f89242540ee208c3098e00000d6568400000000000000a73210331c298cb86428b9126bd4af6a952870cfe3fe5065dc093cf97f3edbb27e9dd15744630440220797922caaa593c4e91fa6b63a38c92ef9f5e2183128918dda166f4292882e137022057702b668d7463ef1d01dad5ee6633bd36f0aa358dacc90d6b68d248672a400f8114f260a758132d3ed27e52d7f55ef0481606f090d4"); +} + +TEST(TWAnySignerRipple, SignNfTokenCancelOffer) { + // https://devnet.xrpl.org/transactions/CBA148308A0D1561E5E8CDF1F2E8D5562C320C221AC4053AA5F495CEF4B5D5D4 + auto key = parse_hex("3e50cc102d8c96abd55f047a536b6425154514ba8abdf5f09335a7c644176c5d"); + Proto::SigningInput input; + + input.mutable_op_nftoken_cancel_offer()->add_token_offers("000b013a95f14b0044f78a264e41713c64b5f89242540ee208c3098e00000d65"); + input.set_fee(10); + input.set_sequence(22857838); + input.set_last_ledger_sequence(22857859); + input.set_account("rPqczdU9bzow966hQKQejsXrMJspM7G4CC"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12001c220000000024015cc86e201b015cc88368400000000000000a7321022250f103fd045edf2e552df2d20aca01a52dc6aedd522d68767f1c744fedb39d74463044022015fff495fc5d61cd71e5815e4d23845ec26f4dc94adb85207feba2c97e19856502207297ec84afc0bb74aa8a20d7254025a82d9b9f177f648845d8c72ee62884ff618114fa84c77f2a5245ef774845d40428d2a6f9603415041320000b013a95f14b0044f78a264e41713c64b5f89242540ee208c3098e00000d65"); +} + } // namespace TW::Ripple::tests diff --git a/tests/chains/XRP/TransactionTests.cpp b/tests/chains/XRP/TransactionTests.cpp index f9015b684da..c2ff41a54b6 100644 --- a/tests/chains/XRP/TransactionTests.cpp +++ b/tests/chains/XRP/TransactionTests.cpp @@ -43,12 +43,14 @@ TEST(RippleTransaction, serialize) { auto account = Address("r9TeThyi5xiuUUrFjtPKZiHcDxs7K9H6Rb"); auto destination = "r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"; auto tx1 = Transaction( - /* amount */25000000, /* fee */10, /* flags */0, /* sequence */2, /* last_ledger_sequence */0, - /* account */account, + /* account */account + ); + tx1.createXrpPayment( + /* amount */25000000, /* destination */destination, /* destination_tag*/0 ); @@ -56,12 +58,14 @@ TEST(RippleTransaction, serialize) { ASSERT_EQ(hex(serialized1), "120000220000000024000000026140000000017d784068400000000000000a81145ccb151f6e9d603f394ae778acf10d3bece874f68314e851bbbe79e328e43d68f43445368133df5fba5a"); auto tx2 = Transaction( - /* amount */200000, /* fee */15, /* flags */0, /* sequence */144, /* last_ledger_sequence */0, - /* account */Address("rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e"), + /* account */Address("rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e") + ); + tx2.createXrpPayment( + /* amount */200000, /* destination */"rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ", /* destination_tag*/0 ); @@ -69,12 +73,14 @@ TEST(RippleTransaction, serialize) { ASSERT_EQ(hex(serialized2), "12000022000000002400000090614000000000030d4068400000000000000f8114aa1bd19d9e87be8069fdbf6843653c43837c03c6831467fe6ec28e0464dd24fb2d62a492aac697cfad02"); auto tx3 = Transaction( - /* amount */25000000, /* fee */12, /* flags */0, /* sequence */1, /* last_ledger_sequence */0, - /* account */Address("r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"), + /* account */Address("r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C") + ); + tx3.createXrpPayment( + /* amount */25000000, /* destination */"rBqSFEFg2B6GBMobtxnU1eLA1zbNC9NDGM", /* destination_tag*/4146942154 ); @@ -82,12 +88,14 @@ TEST(RippleTransaction, serialize) { ASSERT_EQ(hex(serialized3), "120000220000000024000000012ef72d50ca6140000000017d784068400000000000000c8114e851bbbe79e328e43d68f43445368133df5fba5a831476dac5e814cd4aa74142c3ab45e69a900e637aa2"); auto tx4 = Transaction( - /* amount */25000000, /* fee */12, /* flags */0, /* sequence */1, /* last_ledger_sequence */0, - /* account */Address("r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"), + /* account */Address("r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C") + ); + tx4.createXrpPayment( + /* amount */25000000, /* destination */"XVhidoXkozM5DTZFdDnJ5nYC8FPrTuJiyGh1VxSGS6RNJJ5", /* ignore destination_tag*/12345 ); @@ -99,12 +107,14 @@ TEST(RippleTransaction, preImage) { auto account = Address("r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"); auto destination = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; auto tx1 = Transaction( - /* amount */1000, /* fee */10, /* flags */2147483648, /* sequence */1, /* last_ledger_sequence */0, - /* account */account, + /* account */account + ); + tx1.createXrpPayment( + /* amount */1000, /* destination */destination, /* destination_tag*/0 ); From 69b2da9826dfdeb8116be1e5a3747b3e9418592d Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 21 Nov 2022 06:58:46 +0100 Subject: [PATCH 047/426] [Encryption]: add support for aes-256-ctr (#2726) --- .../core/app/utils/TestKeyStore.kt | 11 ++++ include/TrustWalletCore/TWBase.h | 7 ++ include/TrustWalletCore/TWStoredKey.h | 49 +++++++++++++- .../TrustWalletCore/TWStoredKeyEncryption.h | 22 +++++++ src/Keystore/AESParameters.cpp | 41 ++++++++++-- src/Keystore/AESParameters.h | 29 ++++++-- src/Keystore/EncryptionParameters.cpp | 40 +++++++---- src/Keystore/EncryptionParameters.h | 36 +++++----- src/Keystore/StoredKey.cpp | 24 +++---- src/Keystore/StoredKey.h | 13 ++-- src/interface/TWStoredKey.cpp | 27 ++++++-- swift/Sources/KeyStore.swift | 30 ++++----- swift/Tests/Keystore/KeyStoreTests.swift | 24 +++++++ tests/chains/XRP/TWAnySignerTests.cpp | 33 ++++------ tests/common/Keystore/StoredKeyTests.cpp | 51 +++++++++++++- tests/interface/TWStoredKeyTests.cpp | 66 +++++++++++++++++-- wasm/src/keystore/default-impl.ts | 16 +++-- wasm/src/keystore/types.ts | 8 ++- wasm/tests/KeyStore+extension.test.ts | 60 ++++++++++++++++- wasm/tests/KeyStore+fs.test.ts | 62 ++++++++++++++++- 20 files changed, 531 insertions(+), 118 deletions(-) create mode 100644 include/TrustWalletCore/TWStoredKeyEncryption.h diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestKeyStore.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestKeyStore.kt index 3832db95139..599d3369216 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestKeyStore.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestKeyStore.kt @@ -4,6 +4,7 @@ import org.junit.Assert.* import org.junit.Test import wallet.core.jni.StoredKey import wallet.core.jni.CoinType +import wallet.core.jni.StoredKeyEncryption class TestKeyStore { @@ -21,6 +22,16 @@ class TestKeyStore { assertNotNull(result2) } + @Test + fun testDecryptMnemonicAes256() { + val keyStore = StoredKey("Test Wallet", "password".toByteArray(), StoredKeyEncryption.AES256CTR) + val result = keyStore.decryptMnemonic("wrong".toByteArray()) + val result2 = keyStore.decryptMnemonic("password".toByteArray()) + + assertNull(result) + assertNotNull(result2) + } + @Test fun testRemoveCoins() { val password = "password".toByteArray() diff --git a/include/TrustWalletCore/TWBase.h b/include/TrustWalletCore/TWBase.h index 17e618ace45..de300b6779b 100644 --- a/include/TrustWalletCore/TWBase.h +++ b/include/TrustWalletCore/TWBase.h @@ -55,6 +55,13 @@ #define TW_ASSUME_NONNULL_END #endif +#if defined(__cplusplus) && (__cplusplus >= 201402L) +# define TW_DEPRECATED(since) [[deprecated("Since " #since)]] +# define TW_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]] +#else +# define TW_DEPRECATED(since) +# define TW_DEPRECATED_FOR(since, replacement) +#endif #if !__has_feature(nullability) #ifndef _Nullable diff --git a/include/TrustWalletCore/TWStoredKey.h b/include/TrustWalletCore/TWStoredKey.h index 02bd0ae4dad..7d916be9e91 100644 --- a/include/TrustWalletCore/TWStoredKey.h +++ b/include/TrustWalletCore/TWStoredKey.h @@ -13,6 +13,7 @@ #include "TWHDWallet.h" #include "TWPrivateKey.h" #include "TWStoredKeyEncryptionLevel.h" +#include "TWStoredKeyEncryption.h" #include "TWString.h" TW_EXTERN_C_BEGIN @@ -40,6 +41,18 @@ struct TWStoredKey* _Nullable TWStoredKeyLoad(TWString* _Nonnull path); TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin); +/// Imports a private key. +/// +/// \param privateKey Non-null Block of data private key +/// \param name The name of the stored key to import as a non-null string +/// \param password Non-null block of data, password of the stored key +/// \param coin the coin type +/// \param encryption cipher encryption mode +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return Nullptr if the key can't be imported, the stored key otherwise +TW_EXPORT_STATIC_METHOD +struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryption(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption); + /// Imports an HD wallet. /// /// \param mnemonic Non-null bip39 mnemonic @@ -51,6 +64,18 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull priva TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin); +/// Imports an HD wallet. +/// +/// \param mnemonic Non-null bip39 mnemonic +/// \param name The name of the stored key to import as a non-null string +/// \param password Non-null block of data, password of the stored key +/// \param coin the coin type +/// \param encryption cipher encryption mode +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return Nullptr if the key can't be imported, the stored key otherwise +TW_EXPORT_STATIC_METHOD +struct TWStoredKey* _Nullable TWStoredKeyImportHDWalletWithEncryption(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption); + /// Imports a key from JSON. /// /// \param json Json stored key import format as a non-null block of data @@ -59,16 +84,28 @@ struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemo TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nullable TWStoredKeyImportJSON(TWData* _Nonnull json); -/// Creates a new key, with given encryption strength level. Returned object needs to be deleted. +/// Creates a new key, with given encryption strength level. Returned object needs to be deleted. /// /// \param name The name of the key to be stored /// \param password Non-null block of data, password of the stored key /// \param encryptionLevel The level of encryption, see \TWStoredKeyEncryptionLevel /// \note Returned object needs to be deleted with \TWStoredKeyDelete /// \return The stored key as a non-null pointer +TW_DEPRECATED_FOR("3.1.1", "TWStoredKeyCreateLevelAndEncryption") TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nonnull TWStoredKeyCreateLevel(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel); +/// Creates a new key, with given encryption strength level. Returned object needs to be deleted. +/// +/// \param name The name of the key to be stored +/// \param password Non-null block of data, password of the stored key +/// \param encryptionLevel The level of encryption, see \TWStoredKeyEncryptionLevel +/// \param encryption cipher encryption mode +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return The stored key as a non-null pointer +TW_EXPORT_STATIC_METHOD +struct TWStoredKey* _Nonnull TWStoredKeyCreateLevelAndEncryption(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel, enum TWStoredKeyEncryption encryption); + /// Creates a new key. /// /// \deprecated use TWStoredKeyCreateLevel. @@ -78,6 +115,16 @@ struct TWStoredKey* _Nonnull TWStoredKeyCreateLevel(TWString* _Nonnull name, TWD /// \return The stored key as a non-null pointer TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nonnull TWStoredKeyCreate(TWString* _Nonnull name, TWData* _Nonnull password); +/// Creates a new key. +/// +/// \deprecated use TWStoredKeyCreateLevel. +/// \param name The name of the key to be stored +/// \param password Non-null block of data, password of the stored key +/// \param encryption cipher encryption mode +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return The stored key as a non-null pointer +TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nonnull TWStoredKeyCreateEncryption(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryption encryption); + /// Delete a stored key /// /// \param key The key to be deleted diff --git a/include/TrustWalletCore/TWStoredKeyEncryption.h b/include/TrustWalletCore/TWStoredKeyEncryption.h new file mode 100644 index 00000000000..b2db9e795e0 --- /dev/null +++ b/include/TrustWalletCore/TWStoredKeyEncryption.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 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" + +TW_EXTERN_C_BEGIN + +/// Preset encryption kind +TW_EXPORT_ENUM(uint32_t) +enum TWStoredKeyEncryption { + TWStoredKeyEncryptionAes128Ctr = 0, + TWStoredKeyEncryptionAes128Cbc = 1, + TWStoredKeyEncryptionAes192Ctr = 2, + TWStoredKeyEncryptionAes256Ctr = 3, +}; + +TW_EXTERN_C_END diff --git a/src/Keystore/AESParameters.cpp b/src/Keystore/AESParameters.cpp index 6aa7bfdc5b0..22f0df40697 100644 --- a/src/Keystore/AESParameters.cpp +++ b/src/Keystore/AESParameters.cpp @@ -12,20 +12,44 @@ using namespace TW; -namespace TW::Keystore { +namespace { -AESParameters::AESParameters() { - iv = Data(blockSize, 0); +Data generateIv(std::size_t blockSize = TW::Keystore::gBlockSize) { + auto iv = Data(blockSize, 0); random_buffer(iv.data(), blockSize); + return iv; +} + +static TWStoredKeyEncryption getCipher(const std::string& cipher) { + if (cipher == Keystore::gAes128Ctr) { + return TWStoredKeyEncryption::TWStoredKeyEncryptionAes128Ctr; + } else if (cipher == Keystore::gAes192Ctr) { + return TWStoredKeyEncryption::TWStoredKeyEncryptionAes192Ctr; + } else if (cipher == Keystore::gAes256Ctr) { + return TWStoredKeyEncryption::TWStoredKeyEncryptionAes256Ctr; + } + return TWStoredKeyEncryptionAes128Ctr; } +const std::unordered_map gEncryptionRegistry{ + {TWStoredKeyEncryptionAes128Ctr, Keystore::AESParameters{.mKeyLength = Keystore::A128, .mCipher = Keystore::gAes128Ctr, .mCipherEncryption = TWStoredKeyEncryptionAes128Ctr}}, + {TWStoredKeyEncryptionAes128Cbc, Keystore::AESParameters{.mKeyLength = Keystore::A128, .mCipher = Keystore::gAes128Cbc, .mCipherEncryption = TWStoredKeyEncryptionAes128Cbc}}, + {TWStoredKeyEncryptionAes192Ctr, Keystore::AESParameters{.mKeyLength = Keystore::A192, .mCipher = Keystore::gAes192Ctr, .mCipherEncryption = TWStoredKeyEncryptionAes192Ctr}}, + {TWStoredKeyEncryptionAes256Ctr, Keystore::AESParameters{.mKeyLength = Keystore::A256, .mCipher = Keystore::gAes256Ctr, .mCipherEncryption = TWStoredKeyEncryptionAes256Ctr}} +}; +} // namespace + +namespace TW::Keystore { + namespace CodingKeys { static const auto iv = "iv"; } // namespace CodingKeys /// Initializes `AESParameters` with a JSON object. -AESParameters::AESParameters(const nlohmann::json& json) { - iv = parse_hex(json[CodingKeys::iv].get()); +AESParameters AESParameters::AESParametersFromJson(const nlohmann::json& json, const std::string& cipher) { + auto parameters = AESParameters::AESParametersFromEncryption(getCipher(cipher)); + parameters.iv = parse_hex(json[CodingKeys::iv].get()); + return parameters; } /// Saves `this` as a JSON object. @@ -35,4 +59,11 @@ nlohmann::json AESParameters::json() const { return j; } +AESParameters AESParameters::AESParametersFromEncryption(TWStoredKeyEncryption encryption) { + auto parameters = gEncryptionRegistry.at(encryption); + // be sure to regenerate an iv. + parameters.iv = generateIv(); + return parameters; +} + } // namespace TW::Keystore diff --git a/src/Keystore/AESParameters.h b/src/Keystore/AESParameters.h index edae9ab9b4f..878700782ee 100644 --- a/src/Keystore/AESParameters.h +++ b/src/Keystore/AESParameters.h @@ -9,20 +9,37 @@ #include "Data.h" #include +#include namespace TW::Keystore { -// AES128 parameters. -struct AESParameters { - static const std::size_t blockSize = 128 / 8; +enum AESKeySize : std::int32_t { + Uninitialized = 0, + A128 = 16, + A192 = 24, + A256 = 32, +}; +inline constexpr std::size_t gBlockSize{16}; +inline constexpr const char* gAes128Ctr{"aes-128-ctr"}; +inline constexpr const char* gAes128Cbc{"aes-128-cbc"}; +inline constexpr const char* gAes192Ctr{"aes-192-ctr"}; +inline constexpr const char* gAes256Ctr{"aes-256-ctr"}; + +// AES128/192/256 parameters. +struct AESParameters { + // For AES, your block length is always going to be 128 bits/16 bytes + std::int32_t mBlockSize{gBlockSize}; + std::int32_t mKeyLength{A128}; + std::string mCipher{gAes128Ctr}; + TWStoredKeyEncryption mCipherEncryption{TWStoredKeyEncryptionAes128Ctr}; Data iv; - /// Initializes `AESParameters` with a random `iv` for AES 128. - AESParameters(); + /// Initializes `AESParameters` with a encryption cipher. + static AESParameters AESParametersFromEncryption(TWStoredKeyEncryption encryption);; /// Initializes `AESParameters` with a JSON object. - AESParameters(const nlohmann::json& json); + static AESParameters AESParametersFromJson(const nlohmann::json& json, const std::string& cipher); /// Saves `this` as a JSON object. nlohmann::json json() const; diff --git a/src/Keystore/EncryptionParameters.cpp b/src/Keystore/EncryptionParameters.cpp index ad7e6ecf348..9ea7a4fa6d1 100644 --- a/src/Keystore/EncryptionParameters.cpp +++ b/src/Keystore/EncryptionParameters.cpp @@ -40,8 +40,8 @@ static const auto mac = "mac"; } // namespace CodingKeys EncryptionParameters::EncryptionParameters(const nlohmann::json& json) { - cipher = json[CodingKeys::cipher].get(); - cipherParams = AESParameters(json[CodingKeys::cipherParams]); + auto cipher = json[CodingKeys::cipher].get(); + cipherParams = AESParameters::AESParametersFromJson(json[CodingKeys::cipherParams], cipher); auto kdf = json[CodingKeys::kdf].get(); if (kdf == "scrypt") { @@ -53,7 +53,7 @@ EncryptionParameters::EncryptionParameters(const nlohmann::json& json) { nlohmann::json EncryptionParameters::json() const { nlohmann::json j; - j[CodingKeys::cipher] = cipher; + j[CodingKeys::cipher] = cipher(); j[CodingKeys::cipherParams] = cipherParams.json(); if (auto* scryptParams = std::get_if(&kdfParams); scryptParams) { @@ -76,14 +76,25 @@ EncryptedPayload::EncryptedPayload(const Data& password, const Data& data, const scryptParams.desiredKeyLength); aes_encrypt_ctx ctx; - auto result = aes_encrypt_key128(derivedKey.data(), &ctx); + auto result = 0; + switch(this->params.cipherParams.mCipherEncryption) { + case TWStoredKeyEncryptionAes128Ctr: + case TWStoredKeyEncryptionAes128Cbc: + result = aes_encrypt_key128(derivedKey.data(), &ctx); + break; + case TWStoredKeyEncryptionAes192Ctr: + result = aes_encrypt_key192(derivedKey.data(), &ctx); + break; + case TWStoredKeyEncryptionAes256Ctr: + result = aes_encrypt_key256(derivedKey.data(), &ctx); + break; + } assert(result == EXIT_SUCCESS); if (result == EXIT_SUCCESS) { Data iv = this->params.cipherParams.iv; encrypted = Data(data.size()); aes_ctr_encrypt(data.data(), encrypted.data(), static_cast(data.size()), iv.data(), aes_ctr_cbuf_inc, &ctx); - - _mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); + _mac = computeMAC(derivedKey.end() - params.getKeyBytesSize(), derivedKey.end(), encrypted); } } @@ -101,13 +112,13 @@ Data EncryptedPayload::decrypt(const Data& password) const { scrypt(password.data(), password.size(), scryptParams->salt.data(), scryptParams->salt.size(), scryptParams->n, scryptParams->r, scryptParams->p, derivedKey.data(), scryptParams->defaultDesiredKeyLength); - mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); + mac = computeMAC(derivedKey.end() - params.getKeyBytesSize(), derivedKey.end(), encrypted); } else if (auto* pbkdf2Params = std::get_if(¶ms.kdfParams); pbkdf2Params) { derivedKey.resize(pbkdf2Params->defaultDesiredKeyLength); pbkdf2_hmac_sha256(password.data(), static_cast(password.size()), pbkdf2Params->salt.data(), static_cast(pbkdf2Params->salt.size()), pbkdf2Params->iterations, derivedKey.data(), pbkdf2Params->defaultDesiredKeyLength); - mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); + mac = computeMAC(derivedKey.end() - params.getKeyBytesSize(), derivedKey.end(), encrypted); } else { throw DecryptionError::unsupportedKDF; } @@ -118,20 +129,21 @@ Data EncryptedPayload::decrypt(const Data& password) const { Data decrypted(encrypted.size()); Data iv = params.cipherParams.iv; - if (params.cipher == "aes-128-ctr") { + const auto encryption = params.cipherParams.mCipherEncryption; + if (encryption == TWStoredKeyEncryptionAes128Ctr || encryption == TWStoredKeyEncryptionAes256Ctr) { aes_encrypt_ctx ctx; - auto __attribute__((unused)) result = aes_encrypt_key(derivedKey.data(), 16, &ctx); + [[maybe_unused]] auto result = aes_encrypt_key(derivedKey.data(), params.getKeyBytesSize(), &ctx); assert(result != EXIT_FAILURE); aes_ctr_decrypt(encrypted.data(), decrypted.data(), static_cast(encrypted.size()), iv.data(), aes_ctr_cbuf_inc, &ctx); - } else if (params.cipher == "aes-128-cbc") { + } else if (encryption == TWStoredKeyEncryptionAes128Cbc) { aes_decrypt_ctx ctx; - auto __attribute__((unused)) result = aes_decrypt_key(derivedKey.data(), 16, &ctx); + [[maybe_unused]] auto result = aes_decrypt_key(derivedKey.data(), params.getKeyBytesSize(), &ctx); assert(result != EXIT_FAILURE); - for (auto i = 0ul; i < encrypted.size(); i += 16) { - aes_cbc_decrypt(encrypted.data() + i, decrypted.data() + i, 16, iv.data(), &ctx); + for (auto i = 0ul; i < encrypted.size(); i += params.getKeyBytesSize()) { + aes_cbc_decrypt(encrypted.data() + i, decrypted.data() + i, params.getKeyBytesSize(), iv.data(), &ctx); } } else { throw DecryptionError::unsupportedCipher; diff --git a/src/Keystore/EncryptionParameters.h b/src/Keystore/EncryptionParameters.h index 6592b9756cd..a3e3ac59750 100644 --- a/src/Keystore/EncryptionParameters.h +++ b/src/Keystore/EncryptionParameters.h @@ -7,34 +7,40 @@ #pragma once #include "AESParameters.h" +#include "Data.h" #include "PBKDF2Parameters.h" #include "ScryptParameters.h" -#include "Data.h" +#include #include -#include #include #include +#include namespace TW::Keystore { /// Set of parameters used when encoding struct EncryptionParameters { - static EncryptionParameters getPreset(enum TWStoredKeyEncryptionLevel preset) { + static EncryptionParameters getPreset(enum TWStoredKeyEncryptionLevel preset, enum TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr) { switch (preset) { - case TWStoredKeyEncryptionLevelMinimal: - return EncryptionParameters(AESParameters(), ScryptParameters::Minimal); - case TWStoredKeyEncryptionLevelWeak: - case TWStoredKeyEncryptionLevelDefault: - default: - return EncryptionParameters(AESParameters(), ScryptParameters::Weak); - case TWStoredKeyEncryptionLevelStandard: - return EncryptionParameters(AESParameters(), ScryptParameters::Standard); + case TWStoredKeyEncryptionLevelMinimal: + return EncryptionParameters(AESParameters::AESParametersFromEncryption(encryption), ScryptParameters::Minimal); + case TWStoredKeyEncryptionLevelWeak: + case TWStoredKeyEncryptionLevelDefault: + default: + return EncryptionParameters(AESParameters::AESParametersFromEncryption(encryption), ScryptParameters::Weak); + case TWStoredKeyEncryptionLevelStandard: + return EncryptionParameters(AESParameters::AESParametersFromEncryption(encryption), ScryptParameters::Standard); } } - /// Cipher algorithm. - std::string cipher = "aes-128-ctr"; + std::int32_t getKeyBytesSize() const noexcept { + return cipherParams.mKeyLength; + } + + std::string cipher() const noexcept { + return cipherParams.mCipher; + } /// Cipher parameters. AESParameters cipherParams = AESParameters(); @@ -46,8 +52,8 @@ struct EncryptionParameters { /// Initializes with standard values. EncryptionParameters(AESParameters cipherParams, std::variant kdfParams) - : cipherParams(std::move(cipherParams)) - , kdfParams(std::move(kdfParams)) {} + : cipherParams(std::move(cipherParams)), kdfParams(std::move(kdfParams)) { + } /// Initializes with a JSON object. EncryptionParameters(const nlohmann::json& json); diff --git a/src/Keystore/StoredKey.cpp b/src/Keystore/StoredKey.cpp index add26c2ca26..9b761186ce4 100644 --- a/src/Keystore/StoredKey.cpp +++ b/src/Keystore/StoredKey.cpp @@ -26,41 +26,41 @@ using namespace TW; namespace TW::Keystore { -StoredKey StoredKey::createWithMnemonic(const std::string& name, const Data& password, const std::string& mnemonic, TWStoredKeyEncryptionLevel encryptionLevel) { +StoredKey StoredKey::createWithMnemonic(const std::string& name, const Data& password, const std::string& mnemonic, TWStoredKeyEncryptionLevel encryptionLevel, TWStoredKeyEncryption encryption) { if (!Mnemonic::isValid(mnemonic)) { throw std::invalid_argument("Invalid mnemonic"); } Data mnemonicData = TW::Data(mnemonic.begin(), mnemonic.end()); - return StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel); + return StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel, encryption); } -StoredKey StoredKey::createWithMnemonicRandom(const std::string& name, const Data& password, TWStoredKeyEncryptionLevel encryptionLevel) { +StoredKey StoredKey::createWithMnemonicRandom(const std::string& name, const Data& password, TWStoredKeyEncryptionLevel encryptionLevel, TWStoredKeyEncryption encryption) { const auto wallet = TW::HDWallet(128, ""); const auto& mnemonic = wallet.getMnemonic(); assert(Mnemonic::isValid(mnemonic)); Data mnemonicData = TW::Data(mnemonic.begin(), mnemonic.end()); - return StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel); + return StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel, encryption); } -StoredKey StoredKey::createWithMnemonicAddDefaultAddress(const std::string& name, const Data& password, const std::string& mnemonic, TWCoinType coin) { - StoredKey key = createWithMnemonic(name, password, mnemonic, TWStoredKeyEncryptionLevelDefault); +StoredKey StoredKey::createWithMnemonicAddDefaultAddress(const std::string& name, const Data& password, const std::string& mnemonic, TWCoinType coin, TWStoredKeyEncryption encryption) { + StoredKey key = createWithMnemonic(name, password, mnemonic, TWStoredKeyEncryptionLevelDefault, encryption); const auto wallet = key.wallet(password); key.account(coin, &wallet); return key; } -StoredKey StoredKey::createWithPrivateKey(const std::string& name, const Data& password, const Data& privateKeyData) { - return StoredKey(StoredKeyType::privateKey, name, password, privateKeyData, TWStoredKeyEncryptionLevelDefault); +StoredKey StoredKey::createWithPrivateKey(const std::string& name, const Data& password, const Data& privateKeyData, TWStoredKeyEncryption encryption) { + return StoredKey(StoredKeyType::privateKey, name, password, privateKeyData, TWStoredKeyEncryptionLevelDefault, encryption); } -StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData) { +StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData, TWStoredKeyEncryption encryption) { const auto curve = TW::curve(coin); if (!PrivateKey::isValid(privateKeyData, curve)) { throw std::invalid_argument("Invalid private key data"); } - StoredKey key = createWithPrivateKey(name, password, privateKeyData); + StoredKey key = createWithPrivateKey(name, password, privateKeyData, encryption); const auto derivationPath = TW::derivationPath(coin); const auto pubKeyType = TW::publicKeyType(coin); const auto pubKey = PrivateKey(privateKeyData).getPublicKey(pubKeyType); @@ -69,9 +69,9 @@ StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(const std::string& na return key; } -StoredKey::StoredKey(StoredKeyType type, std::string name, const Data& password, const Data& data, TWStoredKeyEncryptionLevel encryptionLevel) +StoredKey::StoredKey(StoredKeyType type, std::string name, const Data& password, const Data& data, TWStoredKeyEncryptionLevel encryptionLevel, TWStoredKeyEncryption encryption) : type(type), id(), name(std::move(name)), accounts() { - const auto encryptionParams = EncryptionParameters::getPreset(encryptionLevel); + const auto encryptionParams = EncryptionParameters::getPreset(encryptionLevel, encryption); payload = EncryptedPayload(password, data, encryptionParams); boost::uuids::random_generator gen; id = boost::lexical_cast(gen()); diff --git a/src/Keystore/StoredKey.h b/src/Keystore/StoredKey.h index 96d3010064b..58a5dca3f77 100644 --- a/src/Keystore/StoredKey.h +++ b/src/Keystore/StoredKey.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -45,23 +46,23 @@ class StoredKey { /// Create a new StoredKey, with the given name, mnemonic and password. /// @throws std::invalid_argument if mnemonic is invalid - static StoredKey createWithMnemonic(const std::string& name, const Data& password, const std::string& mnemonic, TWStoredKeyEncryptionLevel encryptionLevel); + static StoredKey createWithMnemonic(const std::string& name, const Data& password, const std::string& mnemonic, TWStoredKeyEncryptionLevel encryptionLevel, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr); /// Create a new StoredKey, with the given name, mnemonic and password. /// @throws std::invalid_argument if mnemonic is invalid - static StoredKey createWithMnemonicRandom(const std::string& name, const Data& password, TWStoredKeyEncryptionLevel encryptionLevel); + static StoredKey createWithMnemonicRandom(const std::string& name, const Data& password, TWStoredKeyEncryptionLevel encryptionLevel, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr); /// Create a new StoredKey, with the given name, mnemonic and password, and also add the default address for the given coin.. /// @throws std::invalid_argument if mnemonic is invalid - static StoredKey createWithMnemonicAddDefaultAddress(const std::string& name, const Data& password, const std::string& mnemonic, TWCoinType coin); + static StoredKey createWithMnemonicAddDefaultAddress(const std::string& name, const Data& password, const std::string& mnemonic, TWCoinType coin, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr); /// Create a new StoredKey, with the given name and private key. /// @throws std::invalid_argument if privateKeyData is not a valid private key - static StoredKey createWithPrivateKey(const std::string& name, const Data& password, const Data& privateKeyData); + static StoredKey createWithPrivateKey(const std::string& name, const Data& password, const Data& privateKeyData, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr); /// Create a new StoredKey, with the given name and private key, and also add the default address for the given coin.. /// @throws std::invalid_argument if privateKeyData is not a valid private key - static StoredKey createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData); + static StoredKey createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr); /// Create a StoredKey from a JSON object. static StoredKey createWithJson(const nlohmann::json& json); @@ -152,7 +153,7 @@ class StoredKey { /// Initializes a `StoredKey` with a type, an encryption password, and unencrypted data. /// This constructor will encrypt the provided data with default encryption /// parameters. - StoredKey(StoredKeyType type, std::string name, const Data& password, const Data& data, TWStoredKeyEncryptionLevel encryptionLevel); + StoredKey(StoredKeyType type, std::string name, const Data& password, const Data& data, TWStoredKeyEncryptionLevel encryptionLevel, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr); /// Find default account for coin, if exists. If multiple exist, default is returned. /// Optional wallet is needed to derive default address diff --git a/src/interface/TWStoredKey.cpp b/src/interface/TWStoredKey.cpp index bccd49a9a03..d07378fb9e4 100644 --- a/src/interface/TWStoredKey.cpp +++ b/src/interface/TWStoredKey.cpp @@ -25,33 +25,50 @@ struct TWStoredKey* _Nullable TWStoredKeyLoad(TWString* _Nonnull path) { } } -struct TWStoredKey* _Nonnull TWStoredKeyCreateLevel(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel) { +struct TWStoredKey* _Nonnull TWStoredKeyCreateLevelAndEncryption(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel, enum TWStoredKeyEncryption encryption) { const auto& nameString = *reinterpret_cast(name); const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password)); - return new TWStoredKey{ KeyStore::StoredKey::createWithMnemonicRandom(nameString, passwordData, encryptionLevel) }; + return new TWStoredKey{ KeyStore::StoredKey::createWithMnemonicRandom(nameString, passwordData, encryptionLevel, encryption) }; +} + +struct TWStoredKey* _Nonnull TWStoredKeyCreateLevel(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel) { + return TWStoredKeyCreateLevelAndEncryption(name, password, encryptionLevel, TWStoredKeyEncryptionAes128Ctr); +} + +struct TWStoredKey* _Nonnull TWStoredKeyCreateEncryption(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryption encryption) { + return TWStoredKeyCreateLevelAndEncryption(name, password, TWStoredKeyEncryptionLevelDefault, encryption); } struct TWStoredKey* _Nonnull TWStoredKeyCreate(TWString* _Nonnull name, TWData* _Nonnull password) { - return TWStoredKeyCreateLevel(name, password, TWStoredKeyEncryptionLevelDefault); + return TWStoredKeyCreateLevelAndEncryption(name, password, TWStoredKeyEncryptionLevelDefault, TWStoredKeyEncryptionAes128Ctr); } struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin) { + return TWStoredKeyImportPrivateKeyWithEncryption(privateKey, name, password, coin, TWStoredKeyEncryptionAes128Ctr); +} + +struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryption(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption) { try { const auto& privateKeyData = *reinterpret_cast(privateKey); const auto& nameString = *reinterpret_cast(name); const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password)); - return new TWStoredKey{ KeyStore::StoredKey::createWithPrivateKeyAddDefaultAddress(nameString, passwordData, coin, privateKeyData) }; + return new TWStoredKey{ KeyStore::StoredKey::createWithPrivateKeyAddDefaultAddress(nameString, passwordData, coin, privateKeyData, encryption) }; } catch (...) { return nullptr; } } struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin) { + return TWStoredKeyImportHDWalletWithEncryption(mnemonic, name, password, coin, TWStoredKeyEncryptionAes128Ctr); +} + + +struct TWStoredKey* _Nullable TWStoredKeyImportHDWalletWithEncryption(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption) { try { const auto& mnemonicString = *reinterpret_cast(mnemonic); const auto& nameString = *reinterpret_cast(name); const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password)); - return new TWStoredKey{ KeyStore::StoredKey::createWithMnemonicAddDefaultAddress(nameString, passwordData, mnemonicString, coin) }; + return new TWStoredKey{ KeyStore::StoredKey::createWithMnemonicAddDefaultAddress(nameString, passwordData, mnemonicString, coin, encryption) }; } catch (...) { return nullptr; } diff --git a/swift/Sources/KeyStore.swift b/swift/Sources/KeyStore.swift index 815843c87de..0b6c3368d77 100644 --- a/swift/Sources/KeyStore.swift +++ b/swift/Sources/KeyStore.swift @@ -74,8 +74,8 @@ public final class KeyStore { } /// Creates a new wallet. HD default by default - public func createWallet(name: String, password: String, coins: [CoinType]) throws -> Wallet { - let key = StoredKey(name: name, password: Data(password.utf8)) + public func createWallet(name: String, password: String, coins: [CoinType], encryption: StoredKeyEncryption = .aes128Ctr) throws -> Wallet { + let key = StoredKey(name: name, password: Data(password.utf8), encryption: encryption) return try saveCreatedWallet(for: key, password: password, coins: coins) } @@ -158,8 +158,8 @@ public final class KeyStore { /// - password: password to use for the imported private key /// - coin: coin to use for this wallet /// - Returns: new wallet - public func `import`(privateKey: PrivateKey, name: String, password: String, coin: CoinType) throws -> Wallet { - guard let newKey = StoredKey.importPrivateKey(privateKey: privateKey.data, name: name, password: Data(password.utf8), coin: coin) else { + public func `import`(privateKey: PrivateKey, name: String, password: String, coin: CoinType, encryption: StoredKeyEncryption = .aes128Ctr) throws -> Wallet { + guard let newKey = StoredKey.importPrivateKeyWithEncryption(privateKey: privateKey.data, name: name, password: Data(password.utf8), coin: coin, encryption: encryption) else { throw Error.invalidKey } let url = makeAccountURL() @@ -179,8 +179,8 @@ public final class KeyStore { /// - encryptPassword: password to use for encrypting /// - coins: coins to add /// - Returns: new account - public func `import`(mnemonic: String, name: String, encryptPassword: String, coins: [CoinType]) throws -> Wallet { - guard let key = StoredKey.importHDWallet(mnemonic: mnemonic, name: name, password: Data(encryptPassword.utf8), coin: coins.first ?? .ethereum) else { + public func `import`(mnemonic: String, name: String, encryptPassword: String, coins: [CoinType], encryption: StoredKeyEncryption = .aes128Ctr) throws -> Wallet { + guard let key = StoredKey.importHDWalletWithEncryption(mnemonic: mnemonic, name: name, password: Data(encryptPassword.utf8), coin: coins.first ?? .ethereum, encryption: encryption) else { throw Error.invalidMnemonic } let url = makeAccountURL() @@ -201,7 +201,7 @@ public final class KeyStore { /// - password: account password /// - newPassword: password to use for exported key /// - Returns: encrypted JSON key - public func export(wallet: Wallet, password: String, newPassword: String) throws -> Data { + public func export(wallet: Wallet, password: String, newPassword: String, encryption: StoredKeyEncryption = .aes128Ctr) throws -> Data { var privateKeyData = try exportPrivateKey(wallet: wallet, password: password) defer { privateKeyData.resetBytes(in: 0 ..< privateKeyData.count) @@ -211,12 +211,12 @@ public final class KeyStore { throw Error.accountNotFound } - if let mnemonic = checkMnemonic(privateKeyData), let newKey = StoredKey.importHDWallet(mnemonic: mnemonic, name: "", password: Data(newPassword.utf8), coin: coin) { + if let mnemonic = checkMnemonic(privateKeyData), let newKey = StoredKey.importHDWalletWithEncryption(mnemonic: mnemonic, name: "", password: Data(newPassword.utf8), coin: coin, encryption: encryption) { guard let json = newKey.exportJSON() else { throw Error.invalidKey } return json - } else if let newKey = StoredKey.importPrivateKey(privateKey: privateKeyData, name: "", password: Data(newPassword.utf8), coin: coin) { + } else if let newKey = StoredKey.importPrivateKeyWithEncryption(privateKey: privateKeyData, name: "", password: Data(newPassword.utf8), coin: coin, encryption: encryption) { guard let json = newKey.exportJSON() else { throw Error.invalidKey } @@ -269,11 +269,11 @@ public final class KeyStore { /// - wallet: wallet to update /// - password: current password /// - newName: new name - public func update(wallet: Wallet, password: String, newName: String) throws { - try update(wallet: wallet, password: password, newPassword: password, newName: newName) + public func update(wallet: Wallet, password: String, newName: String, encryption: StoredKeyEncryption = .aes128Ctr) throws { + try update(wallet: wallet, password: password, newPassword: password, newName: newName, encryption: encryption) } - private func update(wallet: Wallet, password: String, newPassword: String, newName: String) throws { + private func update(wallet: Wallet, password: String, newPassword: String, newName: String, encryption: StoredKeyEncryption = .aes128Ctr) throws { guard let index = wallets.firstIndex(of: wallet) else { fatalError("Missing wallet") } @@ -291,10 +291,10 @@ public final class KeyStore { } if let mnemonic = checkMnemonic(privateKeyData), - let key = StoredKey.importHDWallet(mnemonic: mnemonic, name: newName, password: Data(newPassword.utf8), coin: coins[0]) { + let key = StoredKey.importHDWalletWithEncryption(mnemonic: mnemonic, name: newName, password: Data(newPassword.utf8), coin: coins[0], encryption: encryption) { wallets[index].key = key - } else if let key = StoredKey.importPrivateKey( - privateKey: privateKeyData, name: newName, password: Data(newPassword.utf8), coin: coins[0]) { + } else if let key = StoredKey.importPrivateKeyWithEncryption( + privateKey: privateKeyData, name: newName, password: Data(newPassword.utf8), coin: coins[0], encryption: encryption) { wallets[index].key = key } else { throw Error.invalidKey diff --git a/swift/Tests/Keystore/KeyStoreTests.swift b/swift/Tests/Keystore/KeyStoreTests.swift index 3ae5529e79e..62d907ccfaf 100755 --- a/swift/Tests/Keystore/KeyStoreTests.swift +++ b/swift/Tests/Keystore/KeyStoreTests.swift @@ -183,6 +183,20 @@ class KeyStoreTests: XCTestCase { XCTAssertNotNil(storedData) XCTAssertNotNil(PrivateKey(data: storedData!)) } + + func testImportPrivateKeyAES256() throws { + let keyStore = try KeyStore(keyDirectory: keyDirectory) + let privateKeyData = Data(hexString: "9cdb5cab19aec3bd0fcd614c5f185e7a1d97634d4225730eba22497dc89a716c")! + let key = StoredKey.importPrivateKeyWithEncryption(privateKey: privateKeyData, name: "name", password: Data("password".utf8), coin: .ethereum, encryption: StoredKeyEncryption.aes256Ctr)! + let json = key.exportJSON()! + + let wallet = try keyStore.import(json: json, name: "name", password: "password", newPassword: "newPassword", coins: [.ethereum]) + let storedData = wallet.key.decryptPrivateKey(password: Data("newPassword".utf8)) + + XCTAssertNotNil(keyStore.keyWallet) + XCTAssertNotNil(storedData) + XCTAssertNotNil(PrivateKey(data: storedData!)) + } func testImportPrivateKey() throws { let keyStore = try KeyStore(keyDirectory: keyDirectory) @@ -208,6 +222,16 @@ class KeyStoreTests: XCTestCase { XCTAssertEqual(wallet.accounts.count, 1) XCTAssertNotNil(keyStore.hdWallet) } + + func testImportWalletAES256() throws { + let keyStore = try KeyStore(keyDirectory: keyDirectory) + let wallet = try keyStore.import(mnemonic: mnemonic, name: "name", encryptPassword: "newPassword", coins: [.ethereum], encryption: .aes256Ctr) + let storedData = wallet.key.decryptMnemonic(password: Data("newPassword".utf8)) + + XCTAssertNotNil(storedData) + XCTAssertEqual(wallet.accounts.count, 1) + XCTAssertNotNil(keyStore.hdWallet) + } func testImportJSON() throws { diff --git a/tests/chains/XRP/TWAnySignerTests.cpp b/tests/chains/XRP/TWAnySignerTests.cpp index 47fcbd9d232..184c8a21137 100644 --- a/tests/chains/XRP/TWAnySignerTests.cpp +++ b/tests/chains/XRP/TWAnySignerTests.cpp @@ -84,25 +84,20 @@ TEST(TWAnySignerRipple, SignTrustSetPayment) { TEST(TWAnySignerRipple, SignTokenPayment0) { // https://testnet.xrpl.org/transactions/8F7820892294598B58CFA2E1101D15ED98C179B25A2BA6DAEB4F5B727CB00D4E - for (auto value : std::vector({"10", "10e0", "10.0", "10.0e0", "1e1", ".1e2", "0.1e2", "100e-1", "10000000000000000e-15", "0.0000000000000001e17"})) { - auto key = parse_hex("4ba5fd2ebf0f5d7e579b3c354c263ebb39cda4093845125786a280301af14e21"); - Proto::SigningInput input; - - input.mutable_op_payment()->mutable_currency_amount()->set_currency("USD"); - input.mutable_op_payment()->mutable_currency_amount()->set_value("10"); - input.mutable_op_payment()->mutable_currency_amount()->set_issuer("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"); - input.set_fee(10); - input.set_sequence(32268645); - input.set_last_ledger_sequence(32268666); - input.set_account("raPAA61ca99bdwNiZs5JJukR5rvkHWvkBX"); - input.mutable_op_payment()->set_destination("rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"); - input.set_private_key(key.data(), key.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeXRP); - - EXPECT_EQ(hex(output.encoded()), "12000022000000002401ec6165201b01ec617a61d4c38d7ea4c6800000000000000000000000000055534400000000004b4e9c06f24296074f7bc48f92a97916c6dc5ea968400000000000000a7321020652a477b0cca8b74d6e68a6a386a836b226101617481b95180eaffbe841b3227446304402203e925caeb05006afb135254e9ae4e46de2019db6c6f68614ef969885063a777602206af110fc29775256fcad8b14974c6a838141d82193192d3b57324fe1079afa1781143b2fa4f36553e5b7a4f54ff9e6883e44b4b0dbb383148132e4e20aecf29090ac428a9c43f230a829220d"); - } + auto key = parse_hex("4ba5fd2ebf0f5d7e579b3c354c263ebb39cda4093845125786a280301af14e21"); + Proto::SigningInput input; + input.mutable_op_payment()->mutable_currency_amount()->set_currency("USD"); + input.mutable_op_payment()->mutable_currency_amount()->set_value("10"); + input.mutable_op_payment()->mutable_currency_amount()->set_issuer("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"); + input.set_fee(10); + input.set_sequence(32268645); + input.set_last_ledger_sequence(32268666); + input.set_account("raPAA61ca99bdwNiZs5JJukR5rvkHWvkBX"); + input.mutable_op_payment()->set_destination("rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"); + input.set_private_key(key.data(), key.size()); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + EXPECT_EQ(hex(output.encoded()), "12000022000000002401ec6165201b01ec617a61d4c38d7ea4c6800000000000000000000000000055534400000000004b4e9c06f24296074f7bc48f92a97916c6dc5ea968400000000000000a7321020652a477b0cca8b74d6e68a6a386a836b226101617481b95180eaffbe841b3227446304402203e925caeb05006afb135254e9ae4e46de2019db6c6f68614ef969885063a777602206af110fc29775256fcad8b14974c6a838141d82193192d3b57324fe1079afa1781143b2fa4f36553e5b7a4f54ff9e6883e44b4b0dbb383148132e4e20aecf29090ac428a9c43f230a829220d"); } TEST(TWAnySignerRipple, SignTokenPayment1) { diff --git a/tests/common/Keystore/StoredKeyTests.cpp b/tests/common/Keystore/StoredKeyTests.cpp index 51cc959968d..677ce21a0e2 100644 --- a/tests/common/Keystore/StoredKeyTests.cpp +++ b/tests/common/Keystore/StoredKeyTests.cpp @@ -84,6 +84,22 @@ TEST(StoredKey, CreateWithMnemonicAddDefaultAddress) { EXPECT_EQ(hex(key.privateKey(coinTypeBc, gPassword).bytes), "d2568511baea8dc347f14c4e0479eb8ebe29eb5f664ed796e755896250ffd11f"); } +TEST(StoredKey, CreateWithMnemonicAddDefaultAddressAes256) { + auto key = StoredKey::createWithMnemonicAddDefaultAddress("name", gPassword, gMnemonic, coinTypeBc, TWStoredKeyEncryptionAes256Ctr); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + auto header = key.payload; + EXPECT_EQ(header.params.cipher(), "aes-256-ctr"); + const Data& mnemo2Data = key.payload.decrypt(gPassword); + + EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(gMnemonic)); + EXPECT_EQ(key.accounts.size(), 1ul); + EXPECT_EQ(key.accounts[0].coin, coinTypeBc); + EXPECT_EQ(key.accounts[0].address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + EXPECT_EQ(key.accounts[0].publicKey, "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006"); + EXPECT_EQ(key.accounts[0].extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); + EXPECT_EQ(hex(key.privateKey(coinTypeBc, gPassword).bytes), "d2568511baea8dc347f14c4e0479eb8ebe29eb5f664ed796e755896250ffd11f"); +} + TEST(StoredKey, CreateWithPrivateKeyAddDefaultAddress) { const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", gPassword, coinTypeBc, privateKey); @@ -99,6 +115,23 @@ TEST(StoredKey, CreateWithPrivateKeyAddDefaultAddress) { EXPECT_EQ(json["version"], 3); } +TEST(StoredKey, CreateWithPrivateKeyAddDefaultAddressAes256) { + const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); + auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", gPassword, coinTypeBc, privateKey, TWStoredKeyEncryptionAes256Ctr); + auto header = key.payload; + EXPECT_EQ(header.params.cipher(), "aes-256-ctr"); + EXPECT_EQ(key.type, StoredKeyType::privateKey); + EXPECT_EQ(key.accounts.size(), 1ul); + EXPECT_EQ(key.accounts[0].coin, coinTypeBc); + EXPECT_EQ(key.accounts[0].address, "bc1q375sq4kl2nv0mlmup3vm8znn4eqwu7mt6hkwhr"); + EXPECT_EQ(hex(key.privateKey(coinTypeBc, gPassword).bytes), hex(privateKey)); + + const auto json = key.json(); + EXPECT_EQ(json["name"], "name"); + EXPECT_EQ(json["type"], "private-key"); + EXPECT_EQ(json["version"], 3); +} + TEST(StoredKey, CreateWithPrivateKeyAddDefaultAddressInvalid) { try { const auto privateKeyInvalid = parse_hex("0001020304"); @@ -345,7 +378,7 @@ TEST(StoredKey, ReadWallet) { const auto header = key.payload; - EXPECT_EQ(header.params.cipher, "aes-128-ctr"); + EXPECT_EQ(header.params.cipher(), "aes-128-ctr"); EXPECT_EQ(hex(header.encrypted), "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c"); EXPECT_EQ(hex(header._mac), "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"); EXPECT_EQ(hex(header.params.cipherParams.iv), "83dbcc02d8ccb40e466191a123791e0e"); @@ -403,6 +436,22 @@ TEST(StoredKey, CreateAccounts) { EXPECT_EQ(key.account(coinTypeBc, &wallet)->extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); } +TEST(StoredKey, CreateAccountsAes256) { + string mnemonicPhrase = "team engine square letter hero song dizzy scrub tornado fabric divert saddle"; + auto key = StoredKey::createWithMnemonic("name", gPassword, mnemonicPhrase, TWStoredKeyEncryptionLevelDefault, TWStoredKeyEncryptionAes256Ctr); + auto header = key.payload; + const auto wallet = key.wallet(gPassword); + + EXPECT_EQ(header.params.cipher(), "aes-256-ctr"); + EXPECT_EQ(key.account(TWCoinTypeEthereum, &wallet)->address, "0x494f60cb6Ac2c8F5E1393aD9FdBdF4Ad589507F7"); + EXPECT_EQ(key.account(TWCoinTypeEthereum, &wallet)->publicKey, "04cc32a479080d83fdcf69966713f0aad1bc1dc3ecf873b034894e84259841bc1c9b122717803e68905220ff54952d3f5ea2ab2698ca31f843addf94ae73fae9fd"); + EXPECT_EQ(key.account(TWCoinTypeEthereum, &wallet)->extendedPublicKey, ""); + + EXPECT_EQ(key.account(coinTypeBc, &wallet)->address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + EXPECT_EQ(key.account(coinTypeBc, &wallet)->publicKey, "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006"); + EXPECT_EQ(key.account(coinTypeBc, &wallet)->extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); +} + TEST(StoredKey, DecodingEthereumAddress) { const auto key = StoredKey::load(testDataPath("key.json")); diff --git a/tests/interface/TWStoredKeyTests.cpp b/tests/interface/TWStoredKeyTests.cpp index 6cfa96c04c8..99f2df1ab54 100644 --- a/tests/interface/TWStoredKeyTests.cpp +++ b/tests/interface/TWStoredKeyTests.cpp @@ -25,19 +25,19 @@ using namespace std; /// Return a StoredKey instance that can be used for further tests. Needs to be deleted at the end. -struct std::shared_ptr createAStoredKey(TWCoinType coin, TWData* password) { +struct std::shared_ptr createAStoredKey(TWCoinType coin, TWData* password, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr) { const auto mnemonic = WRAPS(TWStringCreateWithUTF8Bytes("team engine square letter hero song dizzy scrub tornado fabric divert saddle")); const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name")); - const auto key = WRAP(TWStoredKey, TWStoredKeyImportHDWallet(mnemonic.get(), name.get(), password, coin)); + const auto key = WRAP(TWStoredKey, TWStoredKeyImportHDWalletWithEncryption(mnemonic.get(), name.get(), password, coin, encryption)); return key; } /// Return a StoredKey instance that can be used for further tests. Needs to be deleted at the end. -struct std::shared_ptr createDefaultStoredKey() { +struct std::shared_ptr createDefaultStoredKey(TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr) { const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password")); const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get()))); - return createAStoredKey(TWCoinTypeBitcoin, password.get()); + return createAStoredKey(TWCoinTypeBitcoin, password.get(), encryption); } TEST(TWStoredKey, loadPBKDF2Key) { @@ -57,7 +57,7 @@ TEST(TWStoredKey, createWallet) { const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name")); const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password")); const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get()))); - const auto key = WRAP(TWStoredKey, TWStoredKeyCreateLevel(name.get(), password.get(), TWStoredKeyEncryptionLevelDefault)); + const auto key = WRAP(TWStoredKey, TWStoredKeyCreateLevelAndEncryption(name.get(), password.get(), TWStoredKeyEncryptionLevelDefault, TWStoredKeyEncryptionAes128Ctr)); const auto name2 = WRAPS(TWStoredKeyName(key.get())); EXPECT_EQ(string(TWStringUTF8Bytes(name2.get())), "name"); const auto mnemonic = WRAPS(TWStoredKeyDecryptMnemonic(key.get(), password.get())); @@ -82,6 +82,23 @@ TEST(TWStoredKey, importPrivateKey) { TWPrivateKeyDelete(privateKey3); } +TEST(TWStoredKey, importPrivateKeyAes256) { + const auto privateKeyHex = "3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"; + const auto privateKey = WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex)).get())); + const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name")); + const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password")); + const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get()))); + const auto coin = TWCoinTypeBitcoin; + const auto key = WRAP(TWStoredKey, TWStoredKeyImportPrivateKeyWithEncryption(privateKey.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes256Ctr)); + const auto privateKey2 = WRAPD(TWStoredKeyDecryptPrivateKey(key.get(), password.get())); + EXPECT_EQ(hex(data(TWDataBytes(privateKey2.get()), TWDataSize(privateKey2.get()))), privateKeyHex); + + const auto privateKey3 = TWStoredKeyPrivateKey(key.get(), coin, password.get()); + const auto pkData3 = WRAPD(TWPrivateKeyData(privateKey3)); + EXPECT_EQ(hex(data(TWDataBytes(pkData3.get()), TWDataSize(pkData3.get()))), privateKeyHex); + TWPrivateKeyDelete(privateKey3); +} + TEST(TWStoredKey, importHDWallet) { const auto mnemonic = WRAPS(TWStringCreateWithUTF8Bytes("team engine square letter hero song dizzy scrub tornado fabric divert saddle")); const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name")); @@ -97,6 +114,21 @@ TEST(TWStoredKey, importHDWallet) { EXPECT_EQ(nokey.get(), nullptr); } +TEST(TWStoredKey, importHDWalletAES256) { + const auto mnemonic = WRAPS(TWStringCreateWithUTF8Bytes("team engine square letter hero song dizzy scrub tornado fabric divert saddle")); + const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name")); + const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password")); + const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get()))); + const auto coin = TWCoinTypeBitcoin; + const auto key = WRAP(TWStoredKey, TWStoredKeyImportHDWalletWithEncryption(mnemonic.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes256Ctr)); + EXPECT_TRUE(TWStoredKeyIsMnemonic(key.get())); + + // invalid mnemonic + const auto mnemonicInvalid = WRAPS(TWStringCreateWithUTF8Bytes("_THIS_IS_AN_INVALID_MNEMONIC_")); + const auto nokey = WRAP(TWStoredKey, TWStoredKeyImportHDWalletWithEncryption(mnemonicInvalid.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes256Ctr)); + EXPECT_EQ(nokey.get(), nullptr); +} + TEST(TWStoredKey, addressAddRemove) { const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password")); const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get()))); @@ -184,6 +216,30 @@ TEST(TWStoredKey, exportJSON) { EXPECT_EQ(TWDataGet(json.get(), 0), '{'); } +TEST(TWStoredKey, storeAndImportJSONAES256) { + const auto key = createDefaultStoredKey(TWStoredKeyEncryptionAes256Ctr); + const auto outFileName = string(getTestTempDir() + "/TWStoredKey_store.json"); + const auto outFileNameStr = WRAPS(TWStringCreateWithUTF8Bytes(outFileName.c_str())); + EXPECT_TRUE(TWStoredKeyStore(key.get(), outFileNameStr.get())); + + // read contents of file + ifstream ifs(outFileName); + // get length of file: + ifs.seekg (0, ifs.end); + auto length = ifs.tellg(); + ifs.seekg (0, ifs.beg); + EXPECT_TRUE(length > 20); + + Data json(length); + size_t idx = 0; + // read the slow way, ifs.read gave some false warnings with codacy + while (!ifs.eof() && idx < static_cast(length)) { char c = ifs.get(); json[idx++] = (uint8_t)c; } + + const auto key2 = WRAP(TWStoredKey, TWStoredKeyImportJSON(WRAPD(TWDataCreateWithData(&json)).get())); + const auto name2 = WRAPS(TWStoredKeyName(key2.get())); + EXPECT_EQ(string(TWStringUTF8Bytes(name2.get())), "name"); +} + TEST(TWStoredKey, storeAndImportJSON) { const auto key = createDefaultStoredKey(); const auto outFileName = string(getTestTempDir() + "/TWStoredKey_store.json"); diff --git a/wasm/src/keystore/default-impl.ts b/wasm/src/keystore/default-impl.ts index 91da9b40f16..528b63e5554 100644 --- a/wasm/src/keystore/default-impl.ts +++ b/wasm/src/keystore/default-impl.ts @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -import { WalletCore, CoinType, PrivateKey, StoredKey } from "../wallet-core"; +import {WalletCore, CoinType, PrivateKey, StoredKey, StoredKeyEncryption} from "../wallet-core"; import * as Types from "./types"; export class Default implements Types.IKeyStore { @@ -53,16 +53,17 @@ export class Default implements Types.IKeyStore { mnemonic: string, name: string, password: string, - coins: CoinType[] + coins: CoinType[], + encryption: StoredKeyEncryption ): Promise { return new Promise((resolve, reject) => { - const { Mnemonic, StoredKey, HDWallet } = this.core; + const { Mnemonic, StoredKey, HDWallet, StoredKeyEncryption } = this.core; if (!Mnemonic.isValid(mnemonic)) { throw Types.Error.InvalidMnemonic; } let pass = Buffer.from(password); - let storedKey = StoredKey.importHDWallet(mnemonic, name, pass, coins[0]); + let storedKey = StoredKey.importHDWalletWithEncryption(mnemonic, name, pass, coins[0], encryption); let hdWallet = HDWallet.createWithMnemonic(mnemonic, ""); coins.forEach((coin) => { storedKey.accountForCoin(coin, hdWallet); @@ -82,10 +83,11 @@ export class Default implements Types.IKeyStore { key: Uint8Array, name: string, password: string, - coin: CoinType + coin: CoinType, + encryption: StoredKeyEncryption ): Promise { return new Promise((resolve, reject) => { - const { StoredKey, PrivateKey, Curve } = this.core; + const { StoredKey, PrivateKey, Curve, StoredKeyEncryption } = this.core; // FIXME: get curve from coin if ( @@ -95,7 +97,7 @@ export class Default implements Types.IKeyStore { throw Types.Error.InvalidKey; } let pass = Buffer.from(password); - let storedKey = StoredKey.importPrivateKey(key, name, pass, coin); + let storedKey = StoredKey.importPrivateKeyWithEncryption(key, name, pass, coin, encryption); let wallet = this.mapWallet(storedKey); storedKey.delete(); this.importWallet(wallet) diff --git a/wasm/src/keystore/types.ts b/wasm/src/keystore/types.ts index cc0ce8a21fd..aea0ee3b4ae 100644 --- a/wasm/src/keystore/types.ts +++ b/wasm/src/keystore/types.ts @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -import { CoinType, PrivateKey } from "../wallet-core"; +import { CoinType, PrivateKey, StoredKeyEncryption } from "../wallet-core"; export enum WalletType { Mnemonic = "mnemonic", @@ -54,7 +54,8 @@ export interface IKeyStore { mnemonic: string, name: string, password: string, - coins: CoinType[] + coins: CoinType[], + encryption: StoredKeyEncryption ): Promise; // Import a wallet by private key, name and password @@ -62,7 +63,8 @@ export interface IKeyStore { key: Uint8Array, name: string, password: string, - coin: CoinType + coin: CoinType, + encryption: StoredKeyEncryption ): Promise; // Import a Wallet object directly diff --git a/wasm/tests/KeyStore+extension.test.ts b/wasm/tests/KeyStore+extension.test.ts index fe22c5b7091..cf876dcc448 100644 --- a/wasm/tests/KeyStore+extension.test.ts +++ b/wasm/tests/KeyStore+extension.test.ts @@ -11,7 +11,7 @@ import { ChromeStorageMock } from "./mock"; describe("KeyStore", async () => { it("test ExtensionStorage", async () => { - const { CoinType, HexCoding } = globalThis.core; + const { CoinType, HexCoding, StoredKeyEncryption } = globalThis.core; const mnemonic = globalThis.mnemonic as string; const password = globalThis.password as string; @@ -24,7 +24,7 @@ describe("KeyStore", async () => { var wallet = await keystore.import(mnemonic, "Coolw", password, [ CoinType.bitcoin, - ]); + ], StoredKeyEncryption.aes128Ctr); assert.equal(wallet.name, "Coolw"); assert.equal(wallet.type, "mnemonic"); @@ -65,4 +65,60 @@ describe("KeyStore", async () => { keystore.delete(w.id, password); }); }).timeout(10000); + + it("test ExtensionStorage AES256", async () => { + const { CoinType, HexCoding, StoredKeyEncryption } = globalThis.core; + const mnemonic = globalThis.mnemonic as string; + const password = globalThis.password as string; + + const walletIdsKey = "all-wallet-ids"; + const storage = new KeyStore.ExtensionStorage( + walletIdsKey, + new ChromeStorageMock() + ); + const keystore = new KeyStore.Default(globalThis.core, storage); + + var wallet = await keystore.import(mnemonic, "Coolw", password, [ + CoinType.bitcoin, + ], StoredKeyEncryption.aes256Ctr); + + assert.equal(wallet.name, "Coolw"); + assert.equal(wallet.type, "mnemonic"); + assert.equal(wallet.version, 3); + + const account = wallet.activeAccounts[0]; + const key = await keystore.getKey(wallet.id, password, account); + + assert.equal( + HexCoding.encode(key.data()), + "0xd2568511baea8dc347f14c4e0479eb8ebe29eb5f664ed796e755896250ffd11f" + ); + assert.equal(account.address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + assert.equal( + account.extendedPublicKey, + "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn" + ); + assert.equal( + account.publicKey, + "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006" + ); + + wallet = await keystore.addAccounts(wallet.id, password, [ + CoinType.ethereum, + CoinType.binance, + ]); + + assert.equal(wallet.activeAccounts.length, 3); + assert.isTrue(await keystore.hasWallet(wallet.id)); + assert.isFalse(await keystore.hasWallet("invalid-id")); + + const exported = await keystore.export(wallet.id, password); + assert.equal(exported, mnemonic); + + const wallets = await keystore.loadAll(); + + await wallets.forEach((w) => { + keystore.delete(w.id, password); + }); + }).timeout(10000); }); diff --git a/wasm/tests/KeyStore+fs.test.ts b/wasm/tests/KeyStore+fs.test.ts index 743a9a8e4b4..cdadb92940a 100644 --- a/wasm/tests/KeyStore+fs.test.ts +++ b/wasm/tests/KeyStore+fs.test.ts @@ -11,7 +11,7 @@ import { KeyStore } from "../dist"; describe("KeyStore", async () => { it("test FileSystemStorage", async () => { - const { CoinType, HexCoding } = globalThis.core; + const { CoinType, HexCoding, StoredKeyEncryption } = globalThis.core; const mnemonic = globalThis.mnemonic as string; const password = globalThis.password as string; const testDir = "/tmp/wasm-test"; @@ -23,7 +23,7 @@ describe("KeyStore", async () => { var wallet = await keystore.import(mnemonic, "Coolw", password, [ CoinType.bitcoin, - ]); + ], StoredKeyEncryption.aes128Ctr); const stats = fs.statSync(storage.getFilename(wallet.id)); assert.isTrue(stats.isFile()); @@ -67,4 +67,62 @@ describe("KeyStore", async () => { keystore.delete(w.id, password); }); }).timeout(10000); + + it("test FileSystemStorage AES256", async () => { + const { CoinType, HexCoding, StoredKeyEncryption } = globalThis.core; + const mnemonic = globalThis.mnemonic as string; + const password = globalThis.password as string; + const testDir = "/tmp/wasm-test"; + + fs.mkdirSync(testDir, { recursive: true }); + + const storage = new KeyStore.FileSystemStorage(testDir); + const keystore = new KeyStore.Default(globalThis.core, storage); + + var wallet = await keystore.import(mnemonic, "Coolw", password, [ + CoinType.bitcoin, + ], StoredKeyEncryption.aes256Ctr); + const stats = fs.statSync(storage.getFilename(wallet.id)); + + assert.isTrue(stats.isFile()); + assert.isTrue(stats.size > 0); + assert.equal(wallet.name, "Coolw"); + assert.equal(wallet.type, "mnemonic"); + assert.equal(wallet.version, 3); + + const account = wallet.activeAccounts[0]; + const key = await keystore.getKey(wallet.id, password, account); + + assert.equal( + HexCoding.encode(key.data()), + "0xd2568511baea8dc347f14c4e0479eb8ebe29eb5f664ed796e755896250ffd11f" + ); + assert.equal(account.address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + assert.equal( + account.extendedPublicKey, + "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn" + ); + assert.equal( + account.publicKey, + "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006" + ); + + wallet = await keystore.addAccounts(wallet.id, password, [ + CoinType.ethereum, + CoinType.binance, + ]); + + assert.equal(wallet.activeAccounts.length, 3); + assert.isTrue(await keystore.hasWallet(wallet.id)); + assert.isFalse(await keystore.hasWallet("invalid-id")); + + const exported = await keystore.export(wallet.id, password); + assert.equal(exported, mnemonic); + + const wallets = await keystore.loadAll(); + + await wallets.forEach((w) => { + keystore.delete(w.id, password); + }); + }).timeout(10000); }); From d3c2eee16b154cbb69e8959b01e5e71fbabb1212 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 21 Nov 2022 16:16:14 +0100 Subject: [PATCH 048/426] [emscripten]: use javascript for random generation (#2750) * feat(wasm): use javascript for random generation * feat(wasm): fix per review * add link to libsodium --- wasm/src/Random.cpp | 76 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/wasm/src/Random.cpp b/wasm/src/Random.cpp index d0ea0b2557f..81b5674c4af 100644 --- a/wasm/src/Random.cpp +++ b/wasm/src/Random.cpp @@ -5,20 +5,84 @@ // file LICENSE at the root of the source code distribution tree. // -#include #include #include -#include +#include + +// clang-format off +static uint32_t +javascript_random(void) +{ + return EM_ASM_INT_V({ + return Module.getRandomValue(); + }); +} + +// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/randombytes/randombytes.c#L53 +static void +javascript_stir(void) +{ + EM_ASM({ + if (Module.getRandomValue === undefined) { + try { + var window_ = 'object' === typeof window ? window : self; + var crypto_ = typeof window_.crypto !== 'undefined' ? window_.crypto : window_.msCrypto; + var randomValuesStandard = function() { + var buf = new Uint32Array(1); + crypto_.getRandomValues(buf); + return buf[0] >>> 0; + }; + randomValuesStandard(); + Module.getRandomValue = randomValuesStandard; + } catch (e) { + try { + var crypto = require('crypto'); + var randomValueNodeJS = function() { + var buf = crypto['randomBytes'](4); + return (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) >>> 0; + }; + randomValueNodeJS(); + Module.getRandomValue = randomValueNodeJS; + } catch (e) { + throw 'No secure random number generator found'; + } + } + } + }); +} + + +static void +randombytes_init_if_needed(void) +{ + static bool initialized = false; + if (!initialized) { + javascript_stir(); + initialized = true; + } +} + +static void +javascript_buf(void * const buf, const size_t size) +{ + unsigned char *p = (unsigned char *) buf; + size_t i; + + for (i = (size_t) 0U; i < size; i++) { + p[i] = (unsigned char) javascript_random(); + } +} +// clang-format on extern "C" { uint32_t random32(void) { - std::mt19937 rng(std::random_device{}()); - return rng(); + randombytes_init_if_needed(); + return javascript_random(); } void random_buffer(uint8_t* buf, size_t len) { - std::mt19937 rng(std::random_device{}()); - std::generate_n(buf, len, [&rng]() -> uint8_t { return rng() & 0x000000ff; }); + randombytes_init_if_needed(); + javascript_buf(buf, len); return; } From c61e8663a2cb98aa645fc7d54e28a26d5e60a9ae Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Tue, 22 Nov 2022 14:59:39 +0100 Subject: [PATCH 049/426] [Bitcoin Op_Return/ThorSwap] Proper handling for len 76-80 bytes in two chunks (#2753) --- src/Bitcoin/OpCodes.h | 1 + src/Bitcoin/Script.cpp | 30 ++++++++++++++++----- src/Bitcoin/Script.h | 6 +++++ tests/chains/Bitcoin/BitcoinScriptTests.cpp | 24 +++++++++++++++++ tests/chains/THORChain/SwapTests.cpp | 6 ++--- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/Bitcoin/OpCodes.h b/src/Bitcoin/OpCodes.h index fa797be4b19..dd68248bf1d 100644 --- a/src/Bitcoin/OpCodes.h +++ b/src/Bitcoin/OpCodes.h @@ -10,6 +10,7 @@ enum OpCode { // push value OP_0 = 0x00, OP_FALSE [[maybe_unused]] = OP_0, + // Note: values 0x01 -- 0x4b (1--75) mean that the next N bytes are interpreted as data pushed into the stack OP_PUSHDATA1 = 0x4c, OP_PUSHDATA2 = 0x4d, OP_PUSHDATA4 = 0x4e, diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index 7c8b8da75f4..ac8e7a0b4c7 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -228,11 +228,18 @@ Script Script::buildPayToScriptHash(const Data& scriptHash) { return script; } +// Append to the buffer the length for the upcoming data (push). Supported length range: 0-75 bytes +void pushDataLength(Data& buffer, byte len) { + // Caller contexts make sure len is in the range, not returning error from here + assert(len <= Script::MaxDataPushLength); + buffer.push_back(len); +} + Script Script::buildPayToV0WitnessProgram(const Data& program) { assert(program.size() == 20 || program.size() == 32); Script script; script.bytes.push_back(OP_0); - script.bytes.push_back(static_cast(program.size())); + pushDataLength(script.bytes, static_cast(program.size())); append(script.bytes, program); assert(script.bytes.size() == 22 || script.bytes.size() == 34); return script; @@ -252,23 +259,34 @@ Script Script::buildPayToV1WitnessProgram(const Data& publicKey) { assert(publicKey.size() == 32); Script script; script.bytes.push_back(OP_1); - script.bytes.push_back(static_cast(publicKey.size())); + pushDataLength(script.bytes, static_cast(publicKey.size())); append(script.bytes, publicKey); assert(script.bytes.size() == 34); return script; } Script Script::buildOpReturnScript(const Data& data) { - static const size_t MaxOpReturnLength = 80; if (data.size() > MaxOpReturnLength) { // data too long, cannot fit, fail (do not truncate) - return Script(); + return {}; } assert(data.size() <= MaxOpReturnLength); Script script; script.bytes.push_back(OP_RETURN); - script.bytes.push_back(static_cast(data.size())); - script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + data.size()); + if (data.size() <= MaxDataPushLength) { + // can fit in one push + pushDataLength(script.bytes, static_cast(data.size())); + script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + data.size()); + } else { + // This is the special case of 76-80 bytes, must be put in two data pushes. Use 75 bytes for the first, rest for the 2nd. + const byte push1len = MaxDataPushLength; + const byte push2len = static_cast(data.size()) - push1len; + pushDataLength(script.bytes, push1len); + script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + push1len); + pushDataLength(script.bytes, push2len); + script.bytes.insert(script.bytes.end(), data.begin() + push1len, data.begin() + push1len + push2len); + } + assert(script.bytes.size() <= 83); // max script length, must always hold return script; } diff --git a/src/Bitcoin/Script.h b/src/Bitcoin/Script.h index f4c27c32517..05f973b1020 100644 --- a/src/Bitcoin/Script.h +++ b/src/Bitcoin/Script.h @@ -20,6 +20,12 @@ namespace TW::Bitcoin { class Script { public: + // Maximum length of data constant in one push + static const size_t MaxDataPushLength = 75; + + // Maximum length for OP_RETURN data + static const size_t MaxOpReturnLength = 80; + /// Script raw bytes. Data bytes; diff --git a/tests/chains/Bitcoin/BitcoinScriptTests.cpp b/tests/chains/Bitcoin/BitcoinScriptTests.cpp index 0bdc3d92768..210bd4d4ae7 100644 --- a/tests/chains/Bitcoin/BitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/BitcoinScriptTests.cpp @@ -306,19 +306,43 @@ TEST(BitcoinScript, OpReturn) { { Data data = parse_hex("00010203"); Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(script.bytes.size(), 2 + data.size()); EXPECT_EQ(hex(script.bytes), "6a0400010203"); } { Data data = parse_hex("535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a"); Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(script.bytes.size(), 2 + data.size()); EXPECT_EQ(hex(script.bytes), "6a3b535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a"); } { Data data = Data(69); data.push_back(0xab); Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(script.bytes.size(), 2 + data.size()); EXPECT_EQ(hex(script.bytes), "6a46000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab"); } + { + Data data = Data(74); + data.push_back(0xab); + Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(script.bytes.size(), 2 + data.size()); + EXPECT_EQ(hex(script.bytes), + "6a4b" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab"); + } + { + // >75 bytes, in 2 data pushes + Data data = Data(79); + data.push_back(0xab); + Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(script.bytes.size(), 3 + data.size()); // 1 more byte for the extra data push + EXPECT_EQ(hex(script.bytes), + "6a4b" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "05" + "00000000ab"); + } } TEST(BitcoinScript, OpReturnTooLong) { diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 1f2e5fc5a37..2ca87ecbe1f 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -408,11 +408,11 @@ TEST(THORChainSwap, SwapBtcEthWithAffFee) { "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" "03" // outputs "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" - "209ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "52" "6a503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130" + "0c9ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "53" "6a4b3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a746872056e6d3a3130" // witness "02" - "48" "3045022100801dc46b14eb1b630050d48db27557b66254c3b9439909d864747eafbb12f7e902201fdf5433eaf4968a5596989330bc56513b5769c5c9fd77c41f9bdb55cf8202cd01" + "48" "3045022100ded612f58466e3f5b3d489befef16204750d9a225cca43ffe1808a7dc76bdcd70220750876c848d706a130408f4340449412408843b9b29acf208b6303af6228979501" "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" "00000000" // nLockTime ); From d9f73cb4eff52858eb5f78d23e1007cf6f191f16 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Wed, 23 Nov 2022 11:19:43 +0100 Subject: [PATCH 050/426] Bitcoin Op_Return/ThorSwap: Proper handling for len 76-80 bytes with OP_PUSHDATA1 (#2757) Bitcoin Op_Return: Proper handling for len 76-80 bytes with OP_PUSHDATA1 --- src/Bitcoin/Script.cpp | 29 +++++++++------------ src/Bitcoin/Script.h | 3 --- tests/chains/Bitcoin/BitcoinScriptTests.cpp | 15 ++++++----- tests/chains/THORChain/SwapTests.cpp | 4 +-- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index ac8e7a0b4c7..fcd8f383268 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -229,10 +229,16 @@ Script Script::buildPayToScriptHash(const Data& scriptHash) { } // Append to the buffer the length for the upcoming data (push). Supported length range: 0-75 bytes -void pushDataLength(Data& buffer, byte len) { - // Caller contexts make sure len is in the range, not returning error from here - assert(len <= Script::MaxDataPushLength); - buffer.push_back(len); +void pushDataLength(Data& buffer, size_t len) { + assert(len <= 255); + if (len < static_cast(OP_PUSHDATA1)) { + // up to 75 bytes, simple OP_PUSHBYTES with len + buffer.push_back(static_cast(len)); + return; + } + // 75 < len < 256, OP_PUSHDATA with 1-byte len + buffer.push_back(OP_PUSHDATA1); + buffer.push_back(static_cast(len)); } Script Script::buildPayToV0WitnessProgram(const Data& program) { @@ -273,19 +279,8 @@ Script Script::buildOpReturnScript(const Data& data) { assert(data.size() <= MaxOpReturnLength); Script script; script.bytes.push_back(OP_RETURN); - if (data.size() <= MaxDataPushLength) { - // can fit in one push - pushDataLength(script.bytes, static_cast(data.size())); - script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + data.size()); - } else { - // This is the special case of 76-80 bytes, must be put in two data pushes. Use 75 bytes for the first, rest for the 2nd. - const byte push1len = MaxDataPushLength; - const byte push2len = static_cast(data.size()) - push1len; - pushDataLength(script.bytes, push1len); - script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + push1len); - pushDataLength(script.bytes, push2len); - script.bytes.insert(script.bytes.end(), data.begin() + push1len, data.begin() + push1len + push2len); - } + pushDataLength(script.bytes, data.size()); + script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + data.size()); assert(script.bytes.size() <= 83); // max script length, must always hold return script; } diff --git a/src/Bitcoin/Script.h b/src/Bitcoin/Script.h index 05f973b1020..6befe4f79b1 100644 --- a/src/Bitcoin/Script.h +++ b/src/Bitcoin/Script.h @@ -20,9 +20,6 @@ namespace TW::Bitcoin { class Script { public: - // Maximum length of data constant in one push - static const size_t MaxDataPushLength = 75; - // Maximum length for OP_RETURN data static const size_t MaxOpReturnLength = 80; diff --git a/tests/chains/Bitcoin/BitcoinScriptTests.cpp b/tests/chains/Bitcoin/BitcoinScriptTests.cpp index 210bd4d4ae7..599a0ea44b8 100644 --- a/tests/chains/Bitcoin/BitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/BitcoinScriptTests.cpp @@ -332,16 +332,19 @@ TEST(BitcoinScript, OpReturn) { "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab"); } { - // >75 bytes, in 2 data pushes + // >75 bytes, with OP_PUSHDATA1 Data data = Data(79); data.push_back(0xab); Script script = Script::buildOpReturnScript(data); - EXPECT_EQ(script.bytes.size(), 3 + data.size()); // 1 more byte for the extra data push + EXPECT_EQ(script.bytes.size(), 3 + data.size()); EXPECT_EQ(hex(script.bytes), - "6a4b" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "05" - "00000000ab"); + "6a4c50" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab"); + } + { + // >80 bytes, fails + EXPECT_EQ(hex(Script::buildOpReturnScript(Data(81)).bytes), ""); + EXPECT_EQ(hex(Script::buildOpReturnScript(Data(255)).bytes), ""); } } diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 2ca87ecbe1f..12926d5f58a 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -409,10 +409,10 @@ TEST(THORChainSwap, SwapBtcEthWithAffFee) { "03" // outputs "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" "0c9ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "53" "6a4b3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a746872056e6d3a3130" + "0000000000000000" "53" "6a4c503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130" // witness "02" - "48" "3045022100ded612f58466e3f5b3d489befef16204750d9a225cca43ffe1808a7dc76bdcd70220750876c848d706a130408f4340449412408843b9b29acf208b6303af6228979501" + "47" "3044022056e918d8dea9431057b7b8b7f7c990ff72d653aef296eda9a85e546537e1eaa4022050b64766ea4ce56ecd3325f184d67b20924fd4539cb40bbad916ede1cc26017f01" "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" "00000000" // nLockTime ); From a9970744f3df5296f4e4837942ef27b858317a94 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 24 Nov 2022 13:29:04 +0100 Subject: [PATCH 051/426] [ThorSwap]: Memo shortened + bep2 tokens support (#2754) --- .../thorchain/TestTHORSwapSigning.kt | 8 +- src/THORChain/Swap.cpp | 288 +++++---- src/THORChain/Swap.h | 132 +++- src/THORChain/TWSwap.cpp | 36 +- src/proto/THORChainSwap.proto | 2 +- .../Blockchains/THORChainSwapTests.swift | 18 +- tests/chains/THORChain/SwapTests.cpp | 563 +++++++++++++----- tests/chains/THORChain/TWSwapTests.cpp | 24 +- 8 files changed, 704 insertions(+), 367 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt index e65151148a7..fcc0dd64986 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt @@ -24,7 +24,9 @@ class TestTHORChainSwap { // prepare swap input val input = THORChainSwap.SwapInput.newBuilder() input.apply { - fromChain = THORChainSwap.Chain.ETH + fromAsset = THORChainSwap.Asset.newBuilder().apply { + chain = THORChainSwap.Chain.ETH + }.build() fromAddress = "0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7" toAsset = THORChainSwap.Asset.newBuilder().apply { chain = THORChainSwap.Chain.BNB @@ -42,7 +44,7 @@ class TestTHORChainSwap { // serialize input val inputSerialized = input.build().toByteArray() - assertEquals(Numeric.toHexString(inputSerialized), "0x0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a11353030303030303030303030303030303042063630303030334a2f7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c327463717952023130") + assertEquals(Numeric.toHexString(inputSerialized), "0x0a020802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a11353030303030303030303030303030303042063630303030334a2f7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c327463717952023130") // invoke swap val outputData = buildSwap(inputSerialized) @@ -68,6 +70,6 @@ class TestTHORChainSwap { // sign and encode resulting input val output = AnySigner.sign(txInputFull, ETHEREUM, SigningOutput.parser()) - assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0xf90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000026a027da86e94739f39e8b493f240eb043888a0dd6962a657963ff7fb26f10291ca8a03fed75d6703d5036402be4d0197432725e17fe6a5d3059abdcca74bd7a789cc8") + assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0xf90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000000000026a0ee68bd41da9a9b1ad87fd547e83e4b8022460de024839f4f5f528abc6aecf2aea0402205812d62a075138743f6048ba2a1c073f4a3a14224009a34ee74d3dccef1") } } diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index b799b645023..3be886ab9a8 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -6,17 +6,17 @@ #include "Swap.h" -#include #include "Coin.h" -#include "proto/THORChainSwap.pb.h" +#include + // BTC #include "Bitcoin/SigHashType.h" #include "../proto/Bitcoin.pb.h" // ETH -#include "Ethereum/Address.h" #include "Ethereum/ABI/Function.h" -#include "Ethereum/ABI/ParamBase.h" #include "Ethereum/ABI/ParamAddress.h" +#include "Ethereum/ABI/ParamBase.h" +#include "Ethereum/Address.h" #include "uint256.h" #include "../proto/Ethereum.pb.h" // BNB @@ -32,117 +32,108 @@ namespace TW::THORChainSwap { +static Data ethAddressStringToData(const std::string& asString) { + Data asData(20); + if (asString.empty() || !Ethereum::Address::isValid(asString)) { + return asData; + } + auto address = Ethereum::Address(asString); + std::copy(address.bytes.begin(), address.bytes.end(), asData.data()); + return asData; +} + TWCoinType chainCoinType(Chain chain) { switch (chain) { - case Chain::ETH: return TWCoinTypeEthereum; - case Chain::BNB: return TWCoinTypeBinance; - case Chain::BTC: return TWCoinTypeBitcoin; - case Chain::THOR: - default: - return TWCoinTypeTHORChain; + case Chain::ETH: + return TWCoinTypeEthereum; + case Chain::BNB: + return TWCoinTypeBinance; + case Chain::BTC: + return TWCoinTypeBitcoin; + case Chain::THOR: + default: + return TWCoinTypeTHORChain; } } std::string chainName(Chain chain) { switch (chain) { - case Chain::ETH: return "ETH"; - case Chain::BNB: return "BNB"; - case Chain::BTC: return "BTC"; - case Chain::THOR: - default: - return "THOR"; + case Chain::ETH: + return "ETH"; + case Chain::BNB: + return "BNB"; + case Chain::BTC: + return "BTC"; + case Chain::THOR: + default: + return "THOR"; } } -std::string Swap::buildMemo(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& toAddress, uint64_t limit, const std::string& feeAddress, std::optional feeRate, const std::string& extra) { - std::string prefix = "SWAP"; - if (toChain == Chain::ETH) { - prefix = "="; - } - const auto toCoinToken = (!toTokenId.empty() && toTokenId != "0x0000000000000000000000000000000000000000") ? toTokenId : toSymbol; - std::stringstream memo; - memo << prefix + ":" + chainName(toChain) + "." + toCoinToken + ":" + toAddress + ":" + std::to_string(limit); - - if (!feeAddress.empty() || feeRate.has_value() || !extra.empty()) { - memo << ":"; - if (!feeAddress.empty()) { - memo << feeAddress; - } - if (feeRate.has_value() || !extra.empty()) { - memo << ":"; - if (feeRate.has_value()) { - memo << std::to_string(feeRate.value()); - } - if (!extra.empty()) { - memo << ":"; - memo << extra; - } - } - } - - return memo.str(); -} - bool validateAddress(Chain chain, const std::string& address) { return TW::validateAddress(chainCoinType(chain), address); } -std::tuple Swap::build( - Chain fromChain, - Chain toChain, - const std::string& fromAddress, - const std::string& toSymbol, - const std::string& toTokenId, - const std::string& toAddress, - const std::string& vaultAddress, - const std::string& routerAddress, - const std::string& fromAmount, - const std::string& toAmountLimit, - const std::string& affFeeAddress, - const std::string& affFeeRate, - const std::string& extraMemo -) { - if (!validateAddress(fromChain, fromAddress)) { - return std::make_tuple({}, static_cast(Proto::ErrorCode::Error_Invalid_from_address), "Invalid from address"); +SwapBundled SwapBuilder::build(bool shortened) { + auto fromChain = static_cast(mFromAsset.chain()); + auto toChain = static_cast(mToAsset.chain()); + + if (!validateAddress(fromChain, mFromAddress)) { + return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_from_address), .error = "Invalid from address"}; } - if (!validateAddress(toChain, toAddress)) { - return std::make_tuple({}, static_cast(Proto::ErrorCode::Error_Invalid_to_address), "Invalid to address"); + if (!validateAddress(toChain, mToAddress)) { + return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_to_address), .error = "Invalid to address"}; } - uint64_t fromAmountNum = std::atoll(fromAmount.c_str()); - uint64_t toAmountLimitNum = std::atoll(toAmountLimit.c_str()); - std::optional feeRateNum = affFeeRate.empty() ? std::nullopt : std::optional(std::atoll(affFeeRate.c_str())); - - const auto memo = buildMemo(toChain, toSymbol, toTokenId, toAddress, toAmountLimitNum, affFeeAddress, feeRateNum, extraMemo); + uint64_t fromAmountNum = std::stoull(mFromAmount); + const auto memo = this->buildMemo(shortened); switch (fromChain) { - case Chain::BTC: { - Data out; - auto res = buildBitcoin(toChain, toSymbol, toTokenId, fromAddress, toAddress, vaultAddress, fromAmountNum, memo, out); - return std::make_tuple(std::move(out), std::move(std::get<0>(res)), std::move(std::get<1>(res))); - } + case Chain::BTC: { + return buildBitcoin(fromAmountNum, memo); + case Chain::BNB: + return buildBinance(mFromAsset, fromAmountNum, memo); + case Chain::ETH: + return buildEth(fromAmountNum, memo); + } + default: + return {.status_code = static_cast(Proto::ErrorCode::Error_Unsupported_from_chain), .error = "Unsupported from chain: " + std::to_string(fromChain)}; + } +} +std::string SwapBuilder::buildMemo(bool shortened) noexcept { + uint64_t toAmountLimitNum = std::stoull(mToAmountLimit); + + // Memo: 'SWAP', or shortened '='; see https://dev.thorchain.org/thorchain-dev/concepts/memos + std::string prefix = shortened ? "=" : "SWAP"; + const auto& toChain = static_cast(mToAsset.chain()); + const auto& toTokenId = mToAsset.token_id(); + const auto& toSymbol = mToAsset.symbol(); + const auto toCoinToken = (!toTokenId.empty() && toTokenId != "0x0000000000000000000000000000000000000000") ? toTokenId : toSymbol; + std::stringstream memo; + memo << prefix + ":" + chainName(toChain) + "." + toCoinToken + ":" + mToAddress + ":" + std::to_string(toAmountLimitNum); - case Chain::ETH: { - Data out; - auto res = buildEthereum(toChain, toSymbol, toTokenId, fromAddress, toAddress, vaultAddress, routerAddress, fromAmountNum, memo, out); - return std::make_tuple(std::move(out), std::move(std::get<0>(res)), std::move(std::get<1>(res))); + if (mAffFeeAddress.has_value() || mAffFeeRate.has_value() || mExtraMemo.has_value()) { + memo << ":"; + if (mAffFeeAddress.has_value()) { + memo << mAffFeeAddress.value(); } - - case Chain::BNB: { - Data out; - auto res = buildBinance(toChain, toSymbol, toTokenId, fromAddress, toAddress, vaultAddress, fromAmountNum, memo, out); - return std::make_tuple(std::move(out), std::move(std::get<0>(res)), std::move(std::get<1>(res))); + if (mAffFeeRate.has_value() || mExtraMemo.has_value()) { + memo << ":"; + if (mAffFeeRate.has_value()) { + memo << mAffFeeRate.value(); + } + if (mExtraMemo.has_value()) { + memo << ":" << mExtraMemo.value(); + } } - - case Chain::THOR: - default: - return std::make_tuple({}, static_cast(Proto::ErrorCode::Error_Unsupported_from_chain), "Unsupported from chain: " + std::to_string(toChain)); } + + return memo.str(); } -std::pair Swap::buildBitcoin([[maybe_unused]] Chain toChain, [[maybe_unused]] const std::string& toSymbol, [[maybe_unused]] const std::string& toTokenId, const std::string& fromAddress, [[maybe_unused]] const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out) { +SwapBundled SwapBuilder::buildBitcoin(uint64_t amount, const std::string& memo) { auto input = Bitcoin::Proto::SigningInput(); - + Data out; // Following fields must be set afterwards, before signing ... input.set_hash_type(TWBitcoinSigHashTypeAll); input.set_byte_fee(1); @@ -152,73 +143,19 @@ std::pair Swap::buildBitcoin([[maybe_unused]] Chain toChain, [ // scripts[] // ... end - input.set_amount(amount); - input.set_to_address(vaultAddress); - input.set_change_address(fromAddress); + input.set_amount(static_cast(amount)); + input.set_to_address(mVaultAddress); + input.set_change_address(mFromAddress); input.set_coin_type(TWCoinTypeBitcoin); input.set_output_op_return(memo); auto serialized = input.SerializeAsString(); out.insert(out.end(), serialized.begin(), serialized.end()); - return std::make_pair(0, ""); -} - -Data ethAddressStringToData(const std::string& asString) { - Data asData(20); - if (asString.empty() || !Ethereum::Address::isValid(asString)) { - return asData; - } - auto address = Ethereum::Address(asString); - std::copy(address.bytes.begin(), address.bytes.end(), asData.data()); - return asData; -} - -std::pair Swap::buildEthereum([[maybe_unused]] Chain toChain, [[maybe_unused]] const std::string& toSymbol, const std::string& toTokenId, [[maybe_unused]] const std::string& fromAddress, [[maybe_unused]] const std::string& toAddress, const std::string& vaultAddress, const std::string& routerAddress, uint64_t amount, const std::string& memo, Data& out) { - auto input = Ethereum::Proto::SigningInput(); - - // some sanity check / address conversion - Data vaultAddressBin = ethAddressStringToData(vaultAddress); - if (!Ethereum::Address::isValid(vaultAddress) || vaultAddressBin.size() != Ethereum::Address::size) { - return std::make_pair(static_cast(Proto::ErrorCode::Error_Invalid_vault_address), "Invalid vault address: " + vaultAddress); - } - if (!Ethereum::Address::isValid(routerAddress)) { - return std::make_pair(static_cast(Proto::ErrorCode::Error_Invalid_router_address), "Invalid router address: " + routerAddress); - } - Data toAssetAddressBin = ethAddressStringToData(toTokenId); - - // Following fields must be set afterwards, before signing ... - const auto chainId = store(uint256_t(0)); - input.set_chain_id(chainId.data(), chainId.size()); - const auto nonce = store(uint256_t(0)); - input.set_nonce(nonce.data(), nonce.size()); - const auto gasPrice = store(uint256_t(0)); - input.set_gas_price(gasPrice.data(), gasPrice.size()); - const auto gasLimit = store(uint256_t(0)); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_private_key(""); - // ... end - - input.set_to_address(routerAddress); - auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); - auto func = Ethereum::ABI::Function("deposit", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(toAssetAddressBin), - std::make_shared(uint256_t(amount)), - std::make_shared(memo) - }); - Data payload; - func.encode(payload); - transfer.set_data(payload.data(), payload.size()); - Data amountData = store(uint256_t(amount)); - transfer.set_amount(amountData.data(), amountData.size()); - - auto serialized = input.SerializeAsString(); - out.insert(out.end(), serialized.begin(), serialized.end()); - return std::make_pair(0, ""); + return {.out = std::move(out)}; } - -std::pair Swap::buildBinance([[maybe_unused]] Chain toChain, [[maybe_unused]] const std::string& toSymbol, [[maybe_unused]] const std::string& toTokenId, const std::string& fromAddress, [[maybe_unused]] const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out) { +SwapBundled SwapBuilder::buildBinance(Proto::Asset fromAsset, uint64_t amount, const std::string& memo) { auto input = Binance::Proto::SigningInput(); + Data out; // Following fields must be set afterwards, before signing ... input.set_chain_id(""); @@ -233,18 +170,18 @@ std::pair Swap::buildBinance([[maybe_unused]] Chain toChain, [ auto& order = *input.mutable_send_order(); auto token = Binance::Proto::SendOrder::Token(); - token.set_denom("BNB"); + token.set_denom(fromAsset.token_id().empty() ? "BNB" : fromAsset.token_id()); token.set_amount(amount); { Binance::Address fromAddressBin; - Binance::Address::decode(fromAddress, fromAddressBin); + Binance::Address::decode(mFromAddress, fromAddressBin); auto input_ = order.add_inputs(); input_->set_address(fromAddressBin.getKeyHash().data(), fromAddressBin.getKeyHash().size()); *input_->add_coins() = token; } { Binance::Address vaultAddressBin; - Binance::Address::decode(vaultAddress, vaultAddressBin); + Binance::Address::decode(mVaultAddress, vaultAddressBin); auto output = order.add_outputs(); output->set_address(vaultAddressBin.getKeyHash().data(), vaultAddressBin.getKeyHash().size()); *output->add_coins() = token; @@ -252,7 +189,50 @@ std::pair Swap::buildBinance([[maybe_unused]] Chain toChain, [ auto serialized = input.SerializeAsString(); out.insert(out.end(), serialized.begin(), serialized.end()); - return std::make_pair(0, ""); + return {.out = std::move(out)}; } -} // namespace TW +SwapBundled SwapBuilder::buildEth(uint64_t amount, const std::string& memo) { + Data out; + auto input = Ethereum::Proto::SigningInput(); + const auto& toTokenId = mToAsset.token_id(); + // some sanity check / address conversion + Data vaultAddressBin = ethAddressStringToData(mVaultAddress); + if (!Ethereum::Address::isValid(mVaultAddress) || vaultAddressBin.size() != Ethereum::Address::size) { + return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_vault_address), .error = "Invalid vault address: " + mVaultAddress}; + } + if (!Ethereum::Address::isValid(*mRouterAddress)) { + return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_router_address), .error = "Invalid router address: " + *mRouterAddress}; + } + Data toAssetAddressBin = ethAddressStringToData(toTokenId); + + // Following fields must be set afterwards, before signing ... + const auto chainId = store(uint256_t(0)); + input.set_chain_id(chainId.data(), chainId.size()); + const auto nonce = store(uint256_t(0)); + input.set_nonce(nonce.data(), nonce.size()); + const auto gasPrice = store(uint256_t(0)); + input.set_gas_price(gasPrice.data(), gasPrice.size()); + const auto gasLimit = store(uint256_t(0)); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_private_key(""); + // ... end + + input.set_to_address(*mRouterAddress); + auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); + auto func = Ethereum::ABI::Function("deposit", std::vector>{ + std::make_shared(vaultAddressBin), + std::make_shared(toAssetAddressBin), + std::make_shared(uint256_t(amount)), + std::make_shared(memo)}); + Data payload; + func.encode(payload); + transfer.set_data(payload.data(), payload.size()); + Data amountData = store(uint256_t(amount)); + transfer.set_amount(amountData.data(), amountData.size()); + + auto serialized = input.SerializeAsString(); + out.insert(out.end(), serialized.begin(), serialized.end()); + return {.out = std::move(out)}; +} +} // namespace TW::THORChainSwap diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h index 6f74f047c0e..bd4d253319b 100644 --- a/src/THORChain/Swap.h +++ b/src/THORChain/Swap.h @@ -7,10 +7,11 @@ #pragma once #include "Data.h" +#include "proto/THORChainSwap.pb.h" +#include #include #include -#include namespace TW::THORChainSwap { @@ -22,35 +23,108 @@ enum Chain { BNB = 3, }; -/// Building THORChain cross-chain transactions -class Swap { -public: - /// Logic to build a native transaction on the source chain for a swap - /// Returns serialized SigningInput proto message, on the source chain, - /// and an optional error code + message - static std::tuple build( - Chain fromChain, - Chain toChain, - const std::string& fromAddress, // source address, on source chain, string format - const std::string& toSymbol, // destination coin symbol - const std::string& toTokenId, // destination token ID, on the destination chain, in case destination is a token, empty otherwise - const std::string& toAddress, // destination address, on destination chain, string format - const std::string& vaultAddress, // ThorChainSwap vault, on the source chain. Should be queried afresh, as it may change - const std::string& routerAddress, // ThorChain router, only in case of Ethereum source network - const std::string& fromAmount, // The source amount, as integer in the smallest native unit of the chain - const std::string& toAmountLimit, // The minimum accepted destination amount. Actual destination amount will depend on current rates, limit amount can be used to prevent using very unfavorable rates. - const std::string& affFeeAddress = "", // Optional affiliate fee destination address. A Rune address. - const std::string& affFeeRate = "", // Optional affiliate fee, percentage base points, e.g. 100 means 1%, 0 - 1000, as string. - const std::string& extraMemo = "" // Optional extra custom memo, reserved for later use. - ); - -protected: - static std::pair buildBitcoin(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& fromAddress, const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out); - static std::pair buildEthereum(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& fromAddress, const std::string& toAddress, const std::string& vaultAddress, const std::string& routerAddress, uint64_t amount, const std::string& memo, Data& out); - static std::pair buildBinance(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& fromAddress, const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out); +using SwapErrorCode = int; + +struct SwapBundled { + Data out{}; + SwapErrorCode status_code{0}; + std::string error{""}; +}; + +class SwapBuilder { + Proto::Asset mFromAsset; + Proto::Asset mToAsset; + std::string mFromAddress; + std::string mToAddress; + std::string mVaultAddress; + std::optional mRouterAddress{std::nullopt}; + std::string mFromAmount; + std::string mToAmountLimit{"0"}; + std::optional mAffFeeAddress{std::nullopt}; + std::optional mAffFeeRate{std::nullopt}; + std::optional mExtraMemo{std::nullopt}; + + SwapBundled buildBitcoin(uint64_t amount, const std::string& memo); + SwapBundled buildBinance(Proto::Asset fromAsset, uint64_t amount, const std::string& memo); + SwapBundled buildEth(uint64_t amount, const std::string& memo); public: - static std::string buildMemo(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& toAddress, uint64_t limit, const std::string& feeAddress, std::optional feeRate, const std::string& extra); + SwapBuilder() noexcept = default; + + static SwapBuilder builder() noexcept { return {}; } + + SwapBuilder& from(Proto::Asset fromAsset) noexcept { + mFromAsset = std::move(fromAsset); + return *this; + } + + SwapBuilder& fromAddress(std::string fromAddress) noexcept { + mFromAddress = std::move(fromAddress); + return *this; + } + + SwapBuilder& to(Proto::Asset toAsset) noexcept { + mToAsset = std::move(toAsset); + return *this; + } + + SwapBuilder& toAddress(std::string toAddress) noexcept { + mToAddress = std::move(toAddress); + return *this; + } + + SwapBuilder& vault(std::string vaultAddress) noexcept { + mVaultAddress = std::move(vaultAddress); + return *this; + } + + SwapBuilder& router(std::string router) noexcept { + if (!router.empty()) { + mRouterAddress = std::move(router); + } + return *this; + } + + SwapBuilder& affFeeAddress(std::string affFeeAddress) noexcept { + if (!affFeeAddress.empty()) { + mAffFeeAddress = std::move(affFeeAddress); + } else { + mAffFeeAddress = std::nullopt; + } + return *this; + } + + SwapBuilder& affFeeRate(std::string affFeeRate) noexcept { + if (!affFeeRate.empty()) { + mAffFeeRate = std::move(affFeeRate); + } else { + mAffFeeRate = std::nullopt; + } + return *this; + } + + SwapBuilder& extraMemo(std::string extraMemo) noexcept { + if (!extraMemo.empty()) { + mExtraMemo = std::move(extraMemo); + } else { + mExtraMemo = std::nullopt; + } + return *this; + } + + SwapBuilder& fromAmount(std::string fromAmount) noexcept { + mFromAmount = std::move(fromAmount); + return *this; + } + + SwapBuilder& toAmountLimit(std::string toAmountLimit) noexcept { + mToAmountLimit = std::move(toAmountLimit); + return *this; + } + + std::string buildMemo(bool shortened = true) noexcept; + + SwapBundled build(bool shortened = true); }; -} // namespace TW +} // namespace TW::THORChainSwap diff --git a/src/THORChain/TWSwap.cpp b/src/THORChain/TWSwap.cpp index 2b9c70c567a..cad48c53aa6 100644 --- a/src/THORChain/TWSwap.cpp +++ b/src/THORChain/TWSwap.cpp @@ -22,35 +22,33 @@ TWData* _Nonnull TWTHORChainSwapBuildSwap(TWData* _Nonnull input) { return TWDataCreateWithBytes(outputData.data(), outputData.size()); } - const auto fromChain = inputProto.from_chain(); + const auto fromChain = inputProto.from_asset().chain(); const auto toChain = inputProto.to_asset().chain(); - auto res = THORChainSwap::Swap::build( - static_cast(static_cast(fromChain)), - static_cast(static_cast(toChain)), - inputProto.from_address(), - inputProto.to_asset().symbol(), - inputProto.to_asset().token_id(), - inputProto.to_address(), - inputProto.vault_address(), - inputProto.router_address(), - inputProto.from_amount(), - inputProto.to_amount_limit(), - inputProto.affiliate_fee_address(), - inputProto.affiliate_fee_rate_bp(), - inputProto.extra_memo()); + auto&& [txInput, errorCode, error] = THORChainSwap::SwapBuilder::builder() + .from(inputProto.from_asset()) + .to(inputProto.to_asset()) + .fromAddress(inputProto.from_address()) + .toAddress(inputProto.to_address()) + .vault(inputProto.vault_address()) + .router(inputProto.router_address()) + .fromAmount(inputProto.from_amount()) + .toAmountLimit(inputProto.to_amount_limit()) + .affFeeAddress(inputProto.affiliate_fee_address()) + .affFeeRate(inputProto.affiliate_fee_rate_bp()) + .extraMemo(inputProto.extra_memo()) + .build(); outputProto.set_from_chain(fromChain); outputProto.set_to_chain(toChain); - if (std::get<1>(res) != 0) { + if (errorCode != 0) { // error - outputProto.mutable_error()->set_code(static_cast(std::get<1>(res))); - outputProto.mutable_error()->set_message(std::get<2>(res)); + outputProto.mutable_error()->set_code(static_cast(errorCode)); + outputProto.mutable_error()->set_message(error); } else { // no error outputProto.mutable_error()->set_code(THORChainSwap::Proto::ErrorCode::OK); outputProto.mutable_error()->set_message(""); - const Data& txInput = std::get<0>(res); switch (fromChain) { case THORChainSwap::Proto::BTC: { Bitcoin::Proto::SigningInput btcInput; diff --git a/src/proto/THORChainSwap.proto b/src/proto/THORChainSwap.proto index 90ef10fcb19..0d5d2c248b6 100644 --- a/src/proto/THORChainSwap.proto +++ b/src/proto/THORChainSwap.proto @@ -53,7 +53,7 @@ message Asset { // Input for a swap between source and destination chains; for creating a TX on the source chain. message SwapInput { // Source chain - Chain from_chain = 1; + Asset from_asset = 1; // Source address, on source chain string from_address = 2; diff --git a/swift/Tests/Blockchains/THORChainSwapTests.swift b/swift/Tests/Blockchains/THORChainSwapTests.swift index 56a1d6a851d..73500bd9e25 100644 --- a/swift/Tests/Blockchains/THORChainSwapTests.swift +++ b/swift/Tests/Blockchains/THORChainSwapTests.swift @@ -12,7 +12,9 @@ class THORSwapTests: XCTestCase { func testSignerEthBnbWithFee() throws { // prepare swap input let input = THORChainSwapSwapInput.with { - $0.fromChain = .eth + $0.fromAsset = THORChainSwapAsset.with { + $0.chain = .eth + } $0.fromAddress = "0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7" $0.toAsset = THORChainSwapAsset.with { $0.chain = .bnb @@ -30,7 +32,7 @@ class THORSwapTests: XCTestCase { // serialize input let inputSerialized = try input.serializedData() - XCTAssertEqual(inputSerialized.hexString, "0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a11353030303030303030303030303030303042063630303030334a2f7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c327463717952023130") + XCTAssertEqual(inputSerialized.hexString, "0a020802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a11353030303030303030303030303030303042063630303030334a2f7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c327463717952023130") // invoke swap let outputData = THORChainSwap.buildSwap(input: inputSerialized) @@ -53,13 +55,15 @@ class THORSwapTests: XCTestCase { // sign and encode resulting input let output: EthereumSigningOutput = AnySigner.sign(input: txInput, coin: .ethereum) - XCTAssertEqual(output.encoded.hexString, "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000026a027da86e94739f39e8b493f240eb043888a0dd6962a657963ff7fb26f10291ca8a03fed75d6703d5036402be4d0197432725e17fe6a5d3059abdcca74bd7a789cc8") + XCTAssertEqual(output.encoded.hexString, "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000000000026a0ee68bd41da9a9b1ad87fd547e83e4b8022460de024839f4f5f528abc6aecf2aea0402205812d62a075138743f6048ba2a1c073f4a3a14224009a34ee74d3dccef1") } func testSignerBnbBtc() throws { // prepare swap input let input = THORChainSwapSwapInput.with { - $0.fromChain = .bnb + $0.fromAsset = THORChainSwapAsset.with { + $0.chain = .bnb + } $0.fromAddress = "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx" $0.toAsset = THORChainSwapAsset.with { $0.chain = .btc @@ -75,11 +79,11 @@ class THORSwapTests: XCTestCase { // serialize input let inputSerialized = try input.serializedData() - XCTAssertEqual(inputSerialized.hexString, "0803122a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372781a0708011203425443222a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070382a2a626e62316e396573787577386361377473386c367736366b64683830307330396d7376756c36766c73653a08313030303030303042083130303030303030") + XCTAssertEqual(inputSerialized.hexString, "0a020803122a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372781a0708011203425443222a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070382a2a626e62316e396573787577386361377473386c367736366b64683830307330396d7376756c36766c73653a08313030303030303042083130303030303030") // invoke swap let outputData = THORChainSwap.buildSwap(input: inputSerialized) - XCTAssertEqual(outputData.count, 149) + XCTAssertEqual(outputData.count, 146) // parse result in proto let outputProto = try THORChainSwapSwapOutput(serializedData: outputData) @@ -96,6 +100,6 @@ class THORSwapTests: XCTestCase { // sign and encode resulting input let output: BinanceSigningOutput = AnySigner.sign(input: txInput, coin: .binance) - XCTAssertEqual(output.encoded.hexString, "8002f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e1912404836ee8659caa86771281d3f104424d95977bdedf644ec8585f1674796fde525669a6d446f72da89ee90fb0e064473b0a2159a79630e081592c52948d03d67071a40535741503a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030") + XCTAssertEqual(output.encoded.hexString, "fd01f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e19124008455a84c90e73981ae098578d2ab2b498fe17b0436723c596501b9236d96697514467ed4c22ba8f1cb7506172b368a2ca8be0eb82cb93b5320f938209041f2c1a3d3d3a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030") } } diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 12926d5f58a..fb23e1f3a32 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -4,25 +4,25 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "THORChain/Swap.h" +#include "Binance/Address.h" #include "Bitcoin/Script.h" #include "Bitcoin/SegwitAddress.h" -#include "Ethereum/Address.h" #include "Ethereum/ABI/Function.h" -#include "Ethereum/ABI/ParamBase.h" #include "Ethereum/ABI/ParamAddress.h" -#include "Binance/Address.h" -#include "proto/THORChainSwap.pb.h" +#include "Ethereum/ABI/ParamBase.h" +#include "Ethereum/Address.h" +#include "THORChain/Swap.h" +#include "proto/Binance.pb.h" #include "proto/Bitcoin.pb.h" #include "proto/Ethereum.pb.h" -#include "proto/Binance.pb.h" +#include "proto/THORChainSwap.pb.h" -#include "HexCoding.h" #include "Coin.h" -#include -#include -#include "uint256.h" +#include "HexCoding.h" #include "TestUtilities.h" +#include "uint256.h" +#include +#include #include @@ -41,15 +41,27 @@ const auto VaultEth = "0x1091c4De6a3cF09CdA00AbDAeD42c7c3B69C83EC"; const auto VaultBnb = "bnb1n9esxuw8ca7ts8l6w66kdh800s09msvul6vlse"; const auto RouterEth = "0x42A5Ed456650a09Dc10EBc6361A7480fDd61f27B"; - TEST(THORChainSwap, SwapBtcEth) { - auto res = Swap::build(Chain::BTC, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultBtc, "", "1000000", "140000000000000000"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a473d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313430303030303030303030303030303030"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BTC)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::ETH)); + toAsset.set_symbol("ETH"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Eth) + .vault(VaultBtc) + .fromAmount("1000000") + .toAmountLimit("140000000000000000") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a473d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313430303030303030303030303030303030"); auto tx = Bitcoin::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields EXPECT_EQ(tx.amount(), 1000000); @@ -96,19 +108,33 @@ TEST(THORChainSwap, SwapBtcEth) { } TEST(THORChainSwap, SwapBtcBnb) { - auto res = Swap::build(Chain::BTC, Chain::BNB, Address1Btc, "BNB", "", Address1Bnb, VaultBtc, "", "200000", "140000000"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "080110c09a0c1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a41535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BTC)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Bnb) + .vault(VaultBtc) + .fromAmount("200000") + .toAmountLimit("140000000") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "080110c09a0c1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a3e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030"); auto tx = Bitcoin::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields EXPECT_EQ(tx.amount(), 200000); EXPECT_EQ(tx.to_address(), VaultBtc); EXPECT_EQ(tx.change_address(), Address1Btc); - EXPECT_EQ(tx.output_op_return(), "SWAP:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:140000000"); + EXPECT_EQ(tx.output_op_return(), "=:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:140000000"); EXPECT_EQ(tx.coin_type(), 0ul); EXPECT_EQ(tx.private_key_size(), 0); EXPECT_FALSE(tx.has_plan()); @@ -139,11 +165,11 @@ TEST(THORChainSwap, SwapBtcBnb) { "eb48da786cbd9430bf5ef3d1d3bc7206a4182fd7d5ac3f4e8d05754c3a5cae8e" "00000000" "00" "" "fcffffff" "03" // outputs "400d030000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" - "c08c030000000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "43" "6a41535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030" + "b08d030000000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "40" "6a3e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030" // witness "02" - "47" "3044022071616db5a7fc9e01307ed90eb9a0a30f1441b528ba7631dc8cc8d37448369eed02202e338a0bf857c8dd096173f921fbe0d7d5f154197ab2ca7800079432f6ba486301210" + "48" "3045022100e17d8cf207c79edfb7afa16102842b434e1f908bd9858553fd54970f1a8b4334022059583f89c3a126df0da46d92947bcbe7c265a1bb838b696c0e7ea7fc8761c2bf01210" "21" "e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" "00000000" // nLockTime ); @@ -166,13 +192,27 @@ Data SwapTest_ethAddressStringToData(const std::string& asString) { } TEST(THORChainSwap, SwapEthBnb) { - auto res = Swap::build(Chain::ETH, Chain::BNB, Address1Eth, "BNB", "", Address1Bnb, VaultEth, RouterEth, "50000000000000000", "600003"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "0a01001201002201002a0100422a30783432413545643435363635306130394463313045426336333631413734383066446436316632374252f30132f0010a07b1a2bc2ec5000012e4011fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030330000"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::ETH)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Bnb) + .vault(VaultEth) + .router(RouterEth) + .fromAmount("50000000000000000") + .toAmountLimit("600003") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "0a01001201002201002a0100422a30783432413545643435363635306130394463313045426336333631413734383066446436316632374252f30132f0010a07b1a2bc2ec5000012e4011fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030330000000000"); auto tx = Ethereum::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields EXPECT_EQ(tx.to_address(), RouterEth); @@ -181,21 +221,20 @@ TEST(THORChainSwap, SwapEthBnb) { Data vaultAddressBin = SwapTest_ethAddressStringToData(VaultEth); EXPECT_EQ(hex(vaultAddressBin), "1091c4de6a3cf09cda00abdaed42c7c3b69c83ec"); auto func = Ethereum::ABI::Function("deposit", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(parse_hex("0000000000000000000000000000000000000000")), - std::make_shared(uint256_t(50000000000000000)), - std::make_shared("SWAP:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:600003") - }); + std::make_shared(vaultAddressBin), + std::make_shared(parse_hex("0000000000000000000000000000000000000000")), + std::make_shared(uint256_t(50000000000000000)), + std::make_shared("=:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:600003")}); Data payload; func.encode(payload); EXPECT_EQ(hex(payload), "1fece7b4" - "0000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec" - "0000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000b1a2bc2ec50000" - "0000000000000000000000000000000000000000000000000000000000000080" - "000000000000000000000000000000000000000000000000000000000000003e" - "535741503a424e422e424e423a626e6231757334377764686678303863683937" - "7a6475656833783375356d757266727833306a656372783a3630303030330000"); + "0000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec" + "0000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000b1a2bc2ec50000" + "0000000000000000000000000000000000000000000000000000000000000080" + "000000000000000000000000000000000000000000000000000000000000003b" + "3d3a424e422e424e423a626e62317573343777646866783038636839377a6475" + "656833783375356d757266727833306a656372783a3630303030330000000000"); EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "b1a2bc2ec50000"); EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); @@ -216,20 +255,33 @@ TEST(THORChainSwap, SwapEthBnb) { // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeEthereum); - EXPECT_EQ(hex(output.encoded()), "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000025a06ae104be3201baca38315352f81fac70ca4dd47339981914e64e91149813e780a066a3f0b2c44ddf5a96a38481274f623f552a593d723237d6742185f4885c0064"); + EXPECT_EQ(hex(output.encoded()), "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000000000026a0669563be8a0022fcd32fdf82ccca7dc66012ea28c57e95a2d9348dbf37afc377a03505f5eb041038c565d2f2888207c9dbcad8ca12f10ce5c5bd2ca41de01a9e89"); } TEST(THORChainSwap, SwapBnbBtc) { - auto res = Swap::build(Chain::BNB, Chain::BTC, Address1Bnb, "BTC", "", Address1Btc, VaultBnb, "", "10000000", "10000000"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "2a40535741503a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a313030303030303052480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BNB)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BTC)); + toAsset.set_symbol("BTC"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress(Address1Btc) + .vault(VaultBnb) + .fromAmount("10000000") + .toAmountLimit("10000000") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "2a3d3d3a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a313030303030303052480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204"); auto tx = Binance::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields - EXPECT_EQ(tx.memo(), "SWAP:BTC.BTC:bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8:10000000"); + EXPECT_EQ(tx.memo(), "=:BTC.BTC:bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8:10000000"); ASSERT_TRUE(tx.has_send_order()); ASSERT_EQ(tx.send_order().inputs_size(), 1); ASSERT_EQ(tx.send_order().outputs_size(), 1); @@ -244,17 +296,30 @@ TEST(THORChainSwap, SwapBnbBtc) { // sign and encode resulting input Binance::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeBinance); - EXPECT_EQ(hex(output.encoded()), "8002f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e191240af2117ebd42e31a9562738e9f8933b3b54b59e6305b5675956525e4edb6a6ac65abea614e90959ae388664e2b36bf720024879b6047e174e3cff95f8f364a4e71a40535741503a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030"); + EXPECT_EQ(hex(output.encoded()), "fd01f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e19124086d43e9bdf12508a9a1415f5f970dfa5ff5930dee01d922f99779b63190735ba1d69694bda203b6678939a5c1eab0a52ed32bb67864ec7864de37b333533ae0c1a3d3d3a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030"); } TEST(THORChainSwap, SwapBnbEth) { - auto res = Swap::build(Chain::BNB, Chain::ETH, Address1Bnb, "ETH", "", Address1Eth, VaultBnb, "", "27000000", "123456"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "2a3b3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a31323334353652480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e4210c0f9ef0c12220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e4210c0f9ef0c"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BNB)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::ETH)); + toAsset.set_symbol("ETH"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress(Address1Eth) + .vault(VaultBnb) + .fromAmount("27000000") + .toAmountLimit("123456") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "2a3b3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a31323334353652480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e4210c0f9ef0c12220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e4210c0f9ef0c"); auto tx = Binance::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields EXPECT_EQ(tx.memo(), "=:ETH.ETH:0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7:123456"); @@ -283,16 +348,29 @@ TEST(THORChainSwap, SwapBnbEth) { } TEST(THORChainSwap, SwapBnbRune) { - auto res = Swap::build(Chain::BNB, Chain::THOR, Address1Bnb, "RUNE", "", Address1Thor, VaultBnb, "", "4000000", "121065076"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "2a44535741503a54484f522e52554e453a74686f72317a3533777765376d64366365777a39737177717a6e306161767061756e3067773065786e32723a31323130363530373652480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e42108092f40112220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e42108092f401"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BNB)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::THOR)); + toAsset.set_symbol("RUNE"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress(Address1Thor) + .vault(VaultBnb) + .fromAmount("4000000") + .toAmountLimit("121065076") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "2a413d3a54484f522e52554e453a74686f72317a3533777765376d64366365777a39737177717a6e306161767061756e3067773065786e32723a31323130363530373652480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e42108092f40112220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e42108092f401"); auto tx = Binance::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields - EXPECT_EQ(tx.memo(), "SWAP:THOR.RUNE:thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r:121065076"); + EXPECT_EQ(tx.memo(), "=:THOR.RUNE:thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r:121065076"); ASSERT_TRUE(tx.has_send_order()); ASSERT_EQ(tx.send_order().inputs_size(), 1); ASSERT_EQ(tx.send_order().outputs_size(), 1); @@ -309,35 +387,91 @@ TEST(THORChainSwap, SwapBnbRune) { // sign and encode resulting input Binance::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeBinance); - EXPECT_EQ(hex(output.encoded()), "8a02f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e42108092f40112220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e42108092f40112700a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e191240d91b6655ea4ade62a90cc9b28e43ccd2887dcf1c563e42bbd0d6ae4e825c2c6a1ba7784866810f36b6e098b0c877d1daa48016d0558f7b796b3f0b410107ba2f18ea8f7420041a44535741503a54484f522e52554e453a74686f72317a3533777765376d64366365777a39737177717a6e306161767061756e3067773065786e32723a313231303635303736"); + EXPECT_EQ(hex(output.encoded()), "8702f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e42108092f40112220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e42108092f40112700a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e191240f0bd5a0b4936ce73b1564f737a22cb7cfa3c171a3598b1fe42f6c926c516777042673f3b30148d54b591dcfcb88c2aa04bb87b4b492e8d17c72e4d263f57159018ea8f7420041a413d3a54484f522e52554e453a74686f72317a3533777765376d64366365777a39737177717a6e306161767061756e3067773065786e32723a313231303635303736"); // real transaction: // https://explorer.binance.org/tx/84EE429B35945F0568097527A084532A9DE7BBAB0E6A5562E511CEEFB188DE69 // https://viewblock.io/thorchain/tx/D582E1473FE229F02F162055833C64F49FB4FF515989A4785ED7898560A448FC } +TEST(THORChainSwap, SwapBusdTokenBnb) { + Proto::Asset fromAsset; + fromAsset.set_symbol("BNB"); + fromAsset.set_token_id("BUSD-BD1"); + fromAsset.set_chain(static_cast(Chain::BNB)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress("bnb1gddl87crh47wzynjx3c6pmcclzk7txlkm74x28") + .toAddress("bnb1gddl87crh47wzynjx3c6pmcclzk7txlkm74x28") + .vault("bnb17e9qd0ffrkxsy9pehx7q6hjer730pzq5z4tv82") + .fromAmount("500000000") + .toAmountLimit("719019") + .affFeeAddress("t") + .affFeeRate("0") + .build(false); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "2a42535741503a424e422e424e423a626e62316764646c38376372683437777a796e6a78336336706d63636c7a6b3774786c6b6d37347832383a3731393031393a743a3052540a280a14435bf3fb03bd7ce112723471a0ef18f8ade59bf612100a08425553442d4244311080cab5ee0112280a14f64a06bd291d8d021439b9bc0d5e591fa2f0881412100a08425553442d4244311080cab5ee01"); + + auto tx = Binance::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); + + // check fields + EXPECT_EQ(tx.memo(), "SWAP:BNB.BNB:bnb1gddl87crh47wzynjx3c6pmcclzk7txlkm74x28:719019:t:0"); + ASSERT_TRUE(tx.has_send_order()); + ASSERT_EQ(tx.send_order().inputs_size(), 1); + ASSERT_EQ(tx.send_order().outputs_size(), 1); + EXPECT_EQ(hex(tx.send_order().inputs(0).address()), "435bf3fb03bd7ce112723471a0ef18f8ade59bf6"); + EXPECT_EQ(hex(tx.send_order().outputs(0).address()), "f64a06bd291d8d021439b9bc0d5e591fa2f08814"); + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set private key and few other fields + const Data privateKey = parse_hex("412c379cccf9d792238f0a8bd923604e00c2be11ea1de715945f6a849796362a"); + EXPECT_EQ(Binance::Address(PrivateKey(privateKey).getPublicKey(TWPublicKeyTypeSECP256k1)).string(), "bnb1gddl87crh47wzynjx3c6pmcclzk7txlkm74x28"); + tx.set_private_key(privateKey.data(), privateKey.size()); + tx.set_chain_id("Binance-Chain-Tigris"); + tx.set_account_number(7320332); + tx.set_sequence(2); + + // sign and encode resulting input + Binance::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBinance); + EXPECT_EQ(hex(output.encoded()), "9502f0625dee0a582a2c87fa0a280a14435bf3fb03bd7ce112723471a0ef18f8ade59bf612100a08425553442d4244311080cab5ee0112280a14f64a06bd291d8d021439b9bc0d5e591fa2f0881412100a08425553442d4244311080cab5ee0112710a26eb5ae98721039aa92707d6789692628099f288de219c9c9a0dd179df4e8b1b717191c75fbbfb1240fb41cf3eaaf1286de4be633682c120886b39dcc41690b583f4f08561d660a1677ebda2323e0f22c440c6fe8855d21f1153557b94066ce956363f0a82d1ab3c92188ce6be0320021a42535741503a424e422e424e423a626e62316764646c38376372683437777a796e6a78336336706d63636c7a6b3774786c6b6d37347832383a3731393031393a743a30"); + + // https://viewblock.io/thorchain/tx/1B7E472C7C8D60176FCFD83CAD7DA970EB12B45145C553CD37BD34CABE276C59 + // https://explorer.bnbchain.org/tx/1B7E472C7C8D60176FCFD83CAD7DA970EB12B45145C553CD37BD34CABE276C59 + // https://explorer.bnbchain.org/tx/79D2194584F498CA2D4C391FBD7B158FC94B670703B629CA6F46852BB24234A6 +} + TEST(THORChainSwap, SwapBnbBnbToken) { - auto res = Swap::build( - Chain::BNB, - Chain::BNB, - "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx", - "BNB", - "TWT-8C2", - "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx", - "bnb1qefsjm654cdw94ejj8g4s49w7z8te75veslusz", - "", - "10000000", // 0.1 bnb - "5400000000" // 54.0 twt - ); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "2a46535741503a424e422e5457542d3843323a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3534303030303030303052480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a140653096f54ae1ae2d73291d15854aef08ebcfa8c120a0a03424e421080ade204"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BNB)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + toAsset.set_token_id("TWT-8C2"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress("bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx") + .toAddress("bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx") + .vault("bnb1qefsjm654cdw94ejj8g4s49w7z8te75veslusz") + .fromAmount("10000000") + .toAmountLimit("5400000000") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "2a433d3a424e422e5457542d3843323a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3534303030303030303052480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a140653096f54ae1ae2d73291d15854aef08ebcfa8c120a0a03424e421080ade204"); auto tx = Binance::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields - EXPECT_EQ(tx.memo(), "SWAP:BNB.TWT-8C2:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:5400000000"); + EXPECT_EQ(tx.memo(), "=:BNB.TWT-8C2:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:5400000000"); ASSERT_TRUE(tx.has_send_order()); ASSERT_EQ(tx.send_order().inputs_size(), 1); ASSERT_EQ(tx.send_order().outputs_size(), 1); @@ -356,7 +490,7 @@ TEST(THORChainSwap, SwapBnbBnbToken) { // sign and encode resulting input Binance::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeBinance); - EXPECT_EQ(hex(output.encoded()), "8c02f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a140653096f54ae1ae2d73291d15854aef08ebcfa8c120a0a03424e421080ade20412700a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e1912405fd64a0ed5777f5ea4556624bd096f8b20b6d2b510655e4c928db1ec967e6c7025453882ce7e10138ac92f5d6a949acc5382a5539f81347856c67c4bb678d3c418ea8f7420121a46535741503a424e422e5457542d3843323a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a35343030303030303030"); + EXPECT_EQ(hex(output.encoded()), "8902f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a140653096f54ae1ae2d73291d15854aef08ebcfa8c120a0a03424e421080ade20412700a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e191240918963970aedc528e3a9ba34f37fb544ec18e7d2caade2ebf7b8371928c93e6e0eca072313ddfda393c1340766d5fef00e6b0cb7147ef3382b6303f3a6ca01a318ea8f7420121a433d3a424e422e5457542d3843323a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a35343030303030303030"); // real transaction: // curl -X GET "http://dataseed1.binance.org/broadcast_tx_sync?tx=0x8c02...3030" @@ -366,13 +500,28 @@ TEST(THORChainSwap, SwapBnbBnbToken) { } TEST(THORChainSwap, SwapBtcEthWithAffFee) { - auto res = Swap::build(Chain::BTC, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultBtc, "", "1000000", "140000000000000000", "thrnm", "10"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BTC)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::ETH)); + toAsset.set_symbol("ETH"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Eth) + .vault(VaultBtc) + .fromAmount("1000000") + .toAmountLimit("140000000000000000") + .affFeeAddress("thrnm") + .affFeeRate("10") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130"); auto tx = Bitcoin::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields EXPECT_EQ(tx.amount(), 1000000); @@ -419,13 +568,29 @@ TEST(THORChainSwap, SwapBtcEthWithAffFee) { } TEST(THORChainSwap, SwapEthBnbWithAffFee) { - auto res = Swap::build(Chain::ETH, Chain::BNB, Address1Eth, "BNB", "", Address1Bnb, VaultEth, RouterEth, "50000000000000000", "600003", "tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy", "10"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "0a01001201002201002a0100422a30783432413545643435363635306130394463313045426336333631413734383066446436316632374252b30232b0020a07b1a2bc2ec5000012a4021fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130000000000000000000000000000000"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::ETH)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Bnb) + .vault(VaultEth) + .router(RouterEth) + .fromAmount("50000000000000000") + .toAmountLimit("600003") + .affFeeAddress("tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy") + .affFeeRate("10") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "0a01001201002201002a0100422a30783432413545643435363635306130394463313045426336333631413734383066446436316632374252b30232b0020a07b1a2bc2ec5000012a4021fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130000000000000000000000000000000000000"); auto tx = Ethereum::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields EXPECT_EQ(tx.to_address(), RouterEth); @@ -434,23 +599,22 @@ TEST(THORChainSwap, SwapEthBnbWithAffFee) { Data vaultAddressBin = SwapTest_ethAddressStringToData(VaultEth); EXPECT_EQ(hex(vaultAddressBin), "1091c4de6a3cf09cda00abdaed42c7c3b69c83ec"); auto func = Ethereum::ABI::Function("deposit", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(parse_hex("0000000000000000000000000000000000000000")), - std::make_shared(uint256_t(50000000000000000)), - std::make_shared("SWAP:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:600003:tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy:10") - }); + std::make_shared(vaultAddressBin), + std::make_shared(parse_hex("0000000000000000000000000000000000000000")), + std::make_shared(uint256_t(50000000000000000)), + std::make_shared("=:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:600003:tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy:10")}); Data payload; func.encode(payload); EXPECT_EQ(hex(payload), "1fece7b4" - "0000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec" - "0000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000b1a2bc2ec50000" - "0000000000000000000000000000000000000000000000000000000000000080" - "0000000000000000000000000000000000000000000000000000000000000071" - "535741503a424e422e424e423a626e6231757334377764686678303863683937" - "7a6475656833783375356d757266727833306a656372783a3630303030333a74" - "74686f7231716c3274637179727173676e716c32746371796a326e386b66646d" - "74396c6830797a716c32746371793a3130000000000000000000000000000000"); + "0000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec" + "0000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000b1a2bc2ec50000" + "0000000000000000000000000000000000000000000000000000000000000080" + "000000000000000000000000000000000000000000000000000000000000006e" + "3d3a424e422e424e423a626e62317573343777646866783038636839377a6475" + "656833783375356d757266727833306a656372783a3630303030333a7474686f" + "7231716c3274637179727173676e716c32746371796a326e386b66646d74396c" + "6830797a716c32746371793a3130000000000000000000000000000000000000"); EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "b1a2bc2ec50000"); EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); @@ -472,17 +636,33 @@ TEST(THORChainSwap, SwapEthBnbWithAffFee) { // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeEthereum); - EXPECT_EQ(hex(output.encoded()), "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000071535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000026a027da86e94739f39e8b493f240eb043888a0dd6962a657963ff7fb26f10291ca8a03fed75d6703d5036402be4d0197432725e17fe6a5d3059abdcca74bd7a789cc8"); + EXPECT_EQ(hex(output.encoded()), "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000000000026a0ee68bd41da9a9b1ad87fd547e83e4b8022460de024839f4f5f528abc6aecf2aea0402205812d62a075138743f6048ba2a1c073f4a3a14224009a34ee74d3dccef1"); } TEST(THORChainSwap, SwapBtcNegativeMemoTooLong) { - auto res = Swap::build(Chain::BTC, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultBtc, "", "1000000", "140000000000000000", "affiliate_address", "10", "extra_memo_very_loooooooooooooong"); - ASSERT_EQ(std::get<1>(res), 0); - ASSERT_EQ(std::get<2>(res), ""); - EXPECT_EQ(hex(std::get<0>(res)), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a7e3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a616666696c696174655f616464726573733a31303a65787472615f6d656d6f5f766572795f6c6f6f6f6f6f6f6f6f6f6f6f6f6f6f6e67"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BTC)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::ETH)); + toAsset.set_symbol("ETH"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Eth) + .vault(VaultBtc) + .fromAmount("1000000") + .toAmountLimit("140000000000000000") + .affFeeAddress("affiliate_address") + .affFeeRate("10") + .extraMemo("extra_memo_very_loooooooooooooong") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a7e3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a616666696c696174655f616464726573733a31303a65787472615f6d656d6f5f766572795f6c6f6f6f6f6f6f6f6f6f6f6f6f6f6f6e67"); auto tx = Bitcoin::Proto::SigningInput(); - ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields EXPECT_EQ(tx.amount(), 1000000); @@ -516,64 +696,157 @@ TEST(THORChainSwap, SwapBtcNegativeMemoTooLong) { } TEST(THORChainSwap, Memo) { - EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "", std::nullopt, ""), "SWAP:BTC.BTC:btc123:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "", "bnb123", 1234, "", std::nullopt, ""), "SWAP:BNB.BNB:bnb123:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "", "0xaabbccdd", 1234, "", std::nullopt, ""), "=:ETH.ETH:0xaabbccdd:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "", "0xaabbccdd", 1234, "", std::nullopt, ""), "=:ETH.ETH:0xaabbccdd:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "0x0000000000000000000000000000000000000000", "0xaabbccdd", 1234, "", std::nullopt, ""), "=:ETH.ETH:0xaabbccdd:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "0x4B0F1812e5Df2A09796481Ff14017e6005508003", "0xaabbccdd", 1234, "", std::nullopt, ""), "=:ETH.0x4B0F1812e5Df2A09796481Ff14017e6005508003:0xaabbccdd:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "TWT-8C2", "bnb123", 1234, "", std::nullopt, ""), "SWAP:BNB.TWT-8C2:bnb123:1234"); - EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "feeaddr", std::nullopt, ""), "SWAP:BTC.BTC:btc123:1234:feeaddr"); - EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "feeaddr", 10, ""), "SWAP:BTC.BTC:btc123:1234:feeaddr:10"); - EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "feeaddr", 10, "extramemo"), "SWAP:BTC.BTC:btc123:1234:feeaddr:10:extramemo"); - EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "feeaddr", 0, ""), "SWAP:BTC.BTC:btc123:1234:feeaddr:0"); - EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "", 10, ""), "SWAP:BTC.BTC:btc123:1234::10"); - EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234, "", std::nullopt, "extramemo"), "SWAP:BTC.BTC:btc123:1234:::extramemo"); + Proto::Asset toAssetBTC; + toAssetBTC.set_chain(static_cast(Chain::BTC)); + toAssetBTC.set_symbol("BTC"); + auto builder = SwapBuilder::builder().to(toAssetBTC).toAddress("btc123").toAmountLimit("1234"); + EXPECT_EQ(builder.buildMemo(), "=:BTC.BTC:btc123:1234"); + EXPECT_EQ(builder.affFeeAddress("feeaddr").buildMemo(), "=:BTC.BTC:btc123:1234:feeaddr"); + EXPECT_EQ(builder.affFeeRate("10").buildMemo(), "=:BTC.BTC:btc123:1234:feeaddr:10"); + EXPECT_EQ(builder.extraMemo("extramemo").buildMemo(), "=:BTC.BTC:btc123:1234:feeaddr:10:extramemo"); + EXPECT_EQ(builder.extraMemo("").affFeeRate("0").buildMemo(), "=:BTC.BTC:btc123:1234:feeaddr:0"); + EXPECT_EQ(builder.affFeeAddress("").affFeeRate("10").buildMemo(), "=:BTC.BTC:btc123:1234::10"); + EXPECT_EQ(builder.extraMemo("extramemo").affFeeRate("").buildMemo(), "=:BTC.BTC:btc123:1234:::extramemo"); + + Proto::Asset toAssetETH; + toAssetETH.set_chain(static_cast(Chain::ETH)); + toAssetETH.set_symbol("ETH"); + builder = SwapBuilder::builder().to(toAssetETH).toAddress("0xaabbccdd").toAmountLimit("1234"); + EXPECT_EQ(builder.buildMemo(), "=:ETH.ETH:0xaabbccdd:1234"); + toAssetETH.set_token_id("0x0000000000000000000000000000000000000000"); + EXPECT_EQ(builder.to(toAssetETH).buildMemo(), "=:ETH.ETH:0xaabbccdd:1234"); + toAssetETH.set_token_id("0x4B0F1812e5Df2A09796481Ff14017e6005508003"); + EXPECT_EQ(builder.to(toAssetETH).buildMemo(), "=:ETH.0x4B0F1812e5Df2A09796481Ff14017e6005508003:0xaabbccdd:1234"); + + builder = SwapBuilder::builder().to(toAssetETH).toAddress("bnb123").toAmountLimit("1234"); + Proto::Asset toAssetBNB; + toAssetBNB.set_chain(static_cast(Chain::BNB)); + toAssetBNB.set_symbol("BNB"); + EXPECT_EQ(builder.to(toAssetBNB).buildMemo(), "=:BNB.BNB:bnb123:1234"); + toAssetBNB.set_token_id("TWT-8C2"); + EXPECT_EQ(builder.to(toAssetBNB).buildMemo(), "=:BNB.TWT-8C2:bnb123:1234"); } TEST(THORChainSwap, WrongFromAddress) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BNB)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::ETH)); + toAsset.set_symbol("ETH"); { - auto res = Swap::build(Chain::BNB, Chain::ETH, "DummyAddress", "ETH", "", Address1Eth, VaultEth, "", "100000", "100000"); - EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_from_address); - EXPECT_EQ(std::get<2>(res), "Invalid from address"); + auto && [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress("DummyAddress") + .toAddress(Address1Eth) + .vault(VaultEth) + .fromAmount("1000000") + .toAmountLimit("100000") + .build(); + EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_from_address); + EXPECT_EQ(error, "Invalid from address"); } { - auto res = Swap::build(Chain::BNB, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultEth, "", "100000", "100000"); - EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_from_address); - EXPECT_EQ(std::get<2>(res), "Invalid from address"); + auto && [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Eth) + .vault(VaultEth) + .fromAmount("1000000") + .toAmountLimit("100000") + .build(); + EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_from_address); + EXPECT_EQ(error, "Invalid from address"); } } TEST(THORChainSwap, WrongToAddress) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BNB)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::ETH)); + toAsset.set_symbol("ETH"); { - auto res = Swap::build(Chain::BNB, Chain::ETH, Address1Bnb, "ETH", "", "DummyAddress", VaultEth, "", "100000", "100000"); - EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_to_address); - EXPECT_EQ(std::get<2>(res), "Invalid to address"); + auto && [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress("DummyAddress") + .vault(VaultEth) + .fromAmount("100000") + .toAmountLimit("100000") + .build(); + EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_to_address); + EXPECT_EQ(error, "Invalid to address"); } { - auto res = Swap::build(Chain::BNB, Chain::ETH, Address1Bnb, "ETH", "", Address1Btc, VaultEth, "", "100000", "100000"); - EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_to_address); - EXPECT_EQ(std::get<2>(res), "Invalid to address"); + auto && [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress(Address1Btc) + .vault(VaultEth) + .fromAmount("100000") + .toAmountLimit("100000") + .build(); + EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_to_address); + EXPECT_EQ(error, "Invalid to address"); } } TEST(THORChainSwap, FromRuneNotSupported) { - auto res = Swap::build(Chain::THOR, Chain::BNB, Address1Thor, "BNB", "", Address1Bnb, "", "", "1000", "1000"); - EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Unsupported_from_chain); - EXPECT_EQ(std::get<2>(res), "Unsupported from chain: 3"); + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::THOR)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + auto && [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Thor) + .toAddress(Address1Bnb) + .fromAmount("1000") + .toAmountLimit("1000") + .build(); + EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Unsupported_from_chain); + EXPECT_EQ(error, "Unsupported from chain: 0"); } TEST(THORChainSwap, EthInvalidVault) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::ETH)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); { - auto res = Swap::build(Chain::ETH, Chain::BNB, Address1Eth, "BNB", "", Address1Bnb, "_INVALID_ADDRESS_", RouterEth, "50000000000000000", "600003"); - EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_vault_address); - EXPECT_EQ(std::get<2>(res), "Invalid vault address: _INVALID_ADDRESS_"); + auto && [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Bnb) + .vault("_INVALID_ADDRESS_") + .router(RouterEth) + .fromAmount("50000000000000000") + .toAmountLimit("600003") + .build(); + EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_vault_address); + EXPECT_EQ(error, "Invalid vault address: _INVALID_ADDRESS_"); } { - auto res = Swap::build(Chain::ETH, Chain::BNB, Address1Eth, "BNB", "", Address1Bnb, VaultEth, "_INVALID_ADDRESS_", "50000000000000000", "600003"); - EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_router_address); - EXPECT_EQ(std::get<2>(res), "Invalid router address: _INVALID_ADDRESS_"); + auto && [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Bnb) + .vault(VaultEth) + .router("_INVALID_ADDRESS_") + .fromAmount("50000000000000000") + .toAmountLimit("600003") + .build(); + EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_router_address); + EXPECT_EQ(error, "Invalid router address: _INVALID_ADDRESS_"); } } -} // namespace +} // namespace TW::THORChainSwap diff --git a/tests/chains/THORChain/TWSwapTests.cpp b/tests/chains/THORChain/TWSwapTests.cpp index c82b276e299..3d46e960f03 100644 --- a/tests/chains/THORChain/TWSwapTests.cpp +++ b/tests/chains/THORChain/TWSwapTests.cpp @@ -39,7 +39,9 @@ const Data TestKey1Eth = parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca TEST(TWTHORChainSwap, SwapBtcToEth) { // prepare swap input Proto::SwapInput input; - input.set_from_chain(Proto::BTC); + Proto::Asset fromAsset; + fromAsset.set_chain(Proto::BTC); + *input.mutable_from_asset() = fromAsset; input.set_from_address(Address1Btc); Proto::Asset toAsset; toAsset.set_chain(Proto::ETH); @@ -54,7 +56,7 @@ TEST(TWTHORChainSwap, SwapBtcToEth) { // serialize input const auto inputData_ = input.SerializeAsString(); - EXPECT_EQ(hex(inputData_), "0801122a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070381a0708021203455448222a3078623966353737316332373636346266323238326439386530396437663530636563376362303161372a2a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a373a07313030303030304212313430303030303030303030303030303030"); + EXPECT_EQ(hex(inputData_), "0a020801122a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070381a0708021203455448222a3078623966353737316332373636346266323238326439386530396437663530636563376362303161372a2a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a373a07313030303030304212313430303030303030303030303030303030"); const auto inputTWData_ = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData_.data(), inputData_.size())); // invoke swap @@ -119,7 +121,9 @@ TEST(TWTHORChainSwap, SwapBtcToEth) { TEST(TWTHORChainSwap, SwapEthBnb) { // prepare swap input Proto::SwapInput input; - input.set_from_chain(Proto::ETH); + Proto::Asset fromAsset; + fromAsset.set_chain(Proto::ETH); + *input.mutable_from_asset() = fromAsset; input.set_from_address(Address1Eth); Proto::Asset toAsset; toAsset.set_chain(Proto::BNB); @@ -134,7 +138,7 @@ TEST(TWTHORChainSwap, SwapEthBnb) { // serialize input const auto inputData_ = input.SerializeAsString(); - EXPECT_EQ(hex(inputData_), "0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a1135303030303030303030303030303030304206363030303033"); + EXPECT_EQ(hex(inputData_), "0a020802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a1135303030303030303030303030303030304206363030303033"); const auto inputTWData_ = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData_.data(), inputData_.size())); // invoke swap @@ -167,13 +171,15 @@ TEST(TWTHORChainSwap, SwapEthBnb) { // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(txInput, TWCoinTypeEthereum); - EXPECT_EQ(hex(output.encoded()), "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000025a06ae104be3201baca38315352f81fac70ca4dd47339981914e64e91149813e780a066a3f0b2c44ddf5a96a38481274f623f552a593d723237d6742185f4885c0064"); + EXPECT_EQ(hex(output.encoded()), "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000000000026a0669563be8a0022fcd32fdf82ccca7dc66012ea28c57e95a2d9348dbf37afc377a03505f5eb041038c565d2f2888207c9dbcad8ca12f10ce5c5bd2ca41de01a9e89"); } TEST(TWTHORChainSwap, SwapBnbBtc) { // prepare swap input Proto::SwapInput input; - input.set_from_chain(Proto::BNB); + Proto::Asset fromAsset; + fromAsset.set_chain(Proto::BNB); + *input.mutable_from_asset() = fromAsset; input.set_from_address(Address1Bnb); Proto::Asset toAsset; toAsset.set_chain(Proto::BTC); @@ -188,13 +194,13 @@ TEST(TWTHORChainSwap, SwapBnbBtc) { // serialize input const auto inputData_ = input.SerializeAsString(); - EXPECT_EQ(hex(inputData_), "0803122a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372781a0708011203425443222a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070382a2a626e62316e396573787577386361377473386c367736366b64683830307330396d7376756c36766c73653a08313030303030303042083130303030303030"); + EXPECT_EQ(hex(inputData_), "0a020803122a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372781a0708011203425443222a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070382a2a626e62316e396573787577386361377473386c367736366b64683830307330396d7376756c36766c73653a08313030303030303042083130303030303030"); const auto inputTWData_ = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData_.data(), inputData_.size())); // invoke swap const auto outputTWData_ = WRAPD(TWTHORChainSwapBuildSwap(inputTWData_.get())); const auto outputData = data(TWDataBytes(outputTWData_.get()), TWDataSize(outputTWData_.get())); - EXPECT_EQ(outputData.size(), 149ul); + EXPECT_EQ(outputData.size(), 146ul); // parse result in proto Proto::SwapOutput outputProto; EXPECT_TRUE(outputProto.ParseFromArray(outputData.data(), static_cast(outputData.size()))); @@ -212,7 +218,7 @@ TEST(TWTHORChainSwap, SwapBnbBtc) { // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(txInput, TWCoinTypeBinance); - EXPECT_EQ(hex(output.encoded()), "8002f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e191240af2117ebd42e31a9562738e9f8933b3b54b59e6305b5675956525e4edb6a6ac65abea614e90959ae388664e2b36bf720024879b6047e174e3cff95f8f364a4e71a40535741503a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030"); + EXPECT_EQ(hex(output.encoded()), "fd01f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e19124086d43e9bdf12508a9a1415f5f970dfa5ff5930dee01d922f99779b63190735ba1d69694bda203b6678939a5c1eab0a52ed32bb67864ec7864de37b333533ae0c1a3d3d3a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030"); } TEST(TWTHORChainSwap, NegativeInvalidInput) { From b8f7f1591f5ee7360af27ded94606bb6a29341fc Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Fri, 25 Nov 2022 08:56:55 +0000 Subject: [PATCH 052/426] [Staking]: add NEAR staking (#2734) --- src/NEAR/Serialization.cpp | 7 +-- src/proto/NEAR.proto | 8 ++-- tests/chains/NEAR/TWAnySignerTests.cpp | 61 ++++++++++++++++++++++++- tests/common/HDWallet/HDWalletTests.cpp | 12 +++++ 4 files changed, 80 insertions(+), 8 deletions(-) diff --git a/src/NEAR/Serialization.cpp b/src/NEAR/Serialization.cpp index 9997fc84128..4fe8b43d7db 100644 --- a/src/NEAR/Serialization.cpp +++ b/src/NEAR/Serialization.cpp @@ -24,6 +24,7 @@ static void writeU64(Data& data, uint64_t number) { } static void writeU128(Data& data, const std::string& numberData) { + assert(numberData.size() == 16 && "U128 number should be 16 bytes long"); data.insert(std::end(data), std::begin(numberData), std::end(numberData)); } @@ -107,12 +108,12 @@ static void writeDeleteAccount(Data& data, const Proto::DeleteAccount& deleteAcc static void writeAction(Data& data, const Proto::Action& action) { writeU8(data, action.payload_case() - Proto::Action::kCreateAccount); switch (action.payload_case()) { - case Proto::Action::kTransfer: - writeTransfer(data, action.transfer()); - return; case Proto::Action::kFunctionCall: writeFunctionCall(data, action.function_call()); return; + case Proto::Action::kTransfer: + writeTransfer(data, action.transfer()); + return; case Proto::Action::kStake: writeStake(data, action.stake()); return; diff --git a/src/proto/NEAR.proto b/src/proto/NEAR.proto index a55a9079f94..c557e93ed21 100644 --- a/src/proto/NEAR.proto +++ b/src/proto/NEAR.proto @@ -14,7 +14,7 @@ message PublicKey { // Permissions for a function call message FunctionCallPermission { - // uint128 / little endian byte order + // uint128 / big endian byte order bytes allowance = 1; string receiver_id = 2; @@ -58,19 +58,19 @@ message FunctionCall { // gas uint64 gas = 3; - // uint128 / little endian byte order + // uint128 / big endian byte order bytes deposit = 4; } // Transfer message Transfer { - // amount; uint128 / little endian byte order + // amount; uint128 / big endian byte order bytes deposit = 1; } // Stake message Stake { - // amount; uint128 / little endian byte order + // amount; uint128 / big endian byte order bytes stake = 1; // owner public key diff --git a/tests/chains/NEAR/TWAnySignerTests.cpp b/tests/chains/NEAR/TWAnySignerTests.cpp index afa81ba9a79..3286c0c8475 100644 --- a/tests/chains/NEAR/TWAnySignerTests.cpp +++ b/tests/chains/NEAR/TWAnySignerTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,6 +8,8 @@ #include "proto/NEAR.pb.h" #include "TestUtilities.h" #include +#include "Base58.h" +#include "Base64.h" #include namespace TW::NEAR { @@ -68,4 +70,61 @@ TEST(TWAnySignerNEAR, SignStake) { ASSERT_EQ(hex(output.hash()), "c8aedbf75fcaa9b663a3959d27f1deae809e1923460791471e5219eafecc4ba8"); } +TEST(TWAnySignerNEAR, SignStakeMainnetReplication) { + auto privateKey = Base58::bitcoin.decode("3BPZ9Qu7CviWD4CeKy3DYbNc4suyuBJYnjhVT2oTRCrfb4CQPiTK5tFVdg8Z3ijozxWoxxt9Y1kwkwPntrcc3dom"); + auto blockHash = parse_hex("e78680996127b7a0f3f2343502e442f24366cba5f79cb72f8bc6d0debb26ce24"); + + // 0.1 with 24 decimal precision in big endian + auto amount = parse_hex("000080f64ae1c7022d15000000000000"); + + Proto::SigningInput input; + input.set_signer_id("b8d5df25047841365008f30fb6b30dd820e9a84d869f05623d114e96831f2fbf"); + input.set_nonce(77701544000004); + input.set_receiver_id("avado.poolv1.near"); + input.set_private_key(privateKey.data(), 32); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto& action = *input.add_actions(); + auto& call = *action.mutable_function_call(); + call.set_method_name("deposit_and_stake"); + call.set_args("{}"); + call.set_gas(125000000000000); + call.set_deposit(amount.data(), amount.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNEAR); + + // https://explorer.near.org/transactions/kd7ajFw1CfXB8LiJXvhz5NDS7QpQXkuQraAbhb5MMMq + ASSERT_EQ(Base58::bitcoin.encode(data(output.hash())), "kd7ajFw1CfXB8LiJXvhz5NDS7QpQXkuQraAbhb5MMMq"); + ASSERT_EQ(Base64::encode(data(output.signed_transaction())), "QAAAAGI4ZDVkZjI1MDQ3ODQxMzY1MDA4ZjMwZmI2YjMwZGQ4MjBlOWE4NGQ4NjlmMDU2MjNkMTE0ZTk2ODMxZjJmYmYAzgCT6NK76nb1mB7pToefgkGUHfUe5BKvvr3gW/nq+MgEuu1Mq0YAABEAAABhdmFkby5wb29sdjEubmVhcueGgJlhJ7eg8/I0NQLkQvJDZsul95y3L4vG0N67Js4kAQAAAAIRAAAAZGVwb3NpdF9hbmRfc3Rha2UCAAAAe30A0JjUr3EAAAAAgPZK4ccCLRUAAAAAAAAALNrorr8qTL6u1nlxLpuPa45nFdYmjU96i7CmJP08mVHVzHUaw/bGN30Z3u3o1F2o2yefCBNqO9Ogn9fM25NGCg=="); +} + +TEST(TWAnySignerNEAR, SignUnstakeMainnetReplication) { + auto privateKey = Base58::bitcoin.decode("3BPZ9Qu7CviWD4CeKy3DYbNc4suyuBJYnjhVT2oTRCrfb4CQPiTK5tFVdg8Z3ijozxWoxxt9Y1kwkwPntrcc3dom"); + auto blockHash = Base58::bitcoin.decode("CehJc9uZhqE2m17ZrkqcAog4mxSz6JSvYv1JEK1iBsX9"); + + auto amount = parse_hex("00000000000000000000000000000000"); + + Proto::SigningInput input; + input.set_signer_id("b8d5df25047841365008f30fb6b30dd820e9a84d869f05623d114e96831f2fbf"); + input.set_nonce(77701544000006); + input.set_receiver_id("avado.poolv1.near"); + input.set_private_key(privateKey.data(), 32); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto& action = *input.add_actions(); + auto& call = *action.mutable_function_call(); + call.set_method_name("unstake_all"); + call.set_args("{}"); + call.set_gas(125000000000000); + call.set_deposit(amount.data(), amount.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNEAR); + + // https://explorer.near.org/transactions/DH6QAX3TkY6XtkteorvKBoGT5hA5ADkURZdzrbbKRs8P + ASSERT_EQ(Base58::bitcoin.encode(data(output.hash())), "DH6QAX3TkY6XtkteorvKBoGT5hA5ADkURZdzrbbKRs8P"); + ASSERT_EQ(Base64::encode(data(output.signed_transaction())), "QAAAAGI4ZDVkZjI1MDQ3ODQxMzY1MDA4ZjMwZmI2YjMwZGQ4MjBlOWE4NGQ4NjlmMDU2MjNkMTE0ZTk2ODMxZjJmYmYAzgCT6NK76nb1mB7pToefgkGUHfUe5BKvvr3gW/nq+MgGuu1Mq0YAABEAAABhdmFkby5wb29sdjEubmVhcq0YnhRlt+TTtagkoy0qKn56zAfGhE+jkTJW6PR5k5r8AQAAAAILAAAAdW5zdGFrZV9hbGwCAAAAe30A0JjUr3EAAAAAAAAAAAAAAAAAAAAAAAAABaFP0EkfJU3VQZ4QAiTwq9ebWDJ7jx7TxbA+VGH4hwKX3gWnmDHVve+LK7/UbbffjF/y8vn0KrPxdh3ONAG0Ag=="); +} + } // namespace TW::NEAR diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp index 7430101c7da..3e70073773f 100644 --- a/tests/common/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -11,6 +11,7 @@ #include "Bitcoin/SegwitAddress.h" #include "Ethereum/Address.h" #include "Hedera/DER.h" +#include "NEAR/Address.h" #include "HexCoding.h" #include "PublicKey.h" #include "Hash.h" @@ -456,5 +457,16 @@ TEST(HDWallet, HederaKey) { } } +TEST(HDWallet, NearKey) { + const auto derivPath = "m/44'/397'/0'"; + HDWallet wallet = HDWallet("owner erupt swamp room swift final allow unaware hint identify figure cotton", ""); + { + const auto privateKey = wallet.getKey(TWCoinTypeNEAR, DerivationPath(derivPath)); + EXPECT_EQ(hex(privateKey.bytes), "35e0d9631bd538d5569266abf6be7a9a403ebfda92ddd49b3268e35360a6c2dd"); + const auto p = privateKey.getPublicKey(TWPublicKeyTypeED25519); + EXPECT_EQ(hex(p.bytes), "b8d5df25047841365008f30fb6b30dd820e9a84d869f05623d114e96831f2fbf"); + EXPECT_EQ(NEAR::Address(p).string(), "b8d5df25047841365008f30fb6b30dd820e9a84d869f05623d114e96831f2fbf"); + } +} } // namespace From f96c62337b357c193703cd7c257c884d5fd1b2c1 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 29 Nov 2022 03:05:52 +0100 Subject: [PATCH 053/426] fix(swift): warning public key (#2766) --- include/TrustWalletCore/TWCoinType.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index d740aa310f5..9033f4ea806 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -21,6 +21,9 @@ TW_EXTERN_C_BEGIN /// Represents a private key. struct TWPrivateKey; +/// Represents a public key. +struct TWPublicKey; + /// Coin type for Level 2 of BIP44. /// /// \see https://github.com/satoshilabs/slips/blob/master/slip-0044.md From 49fdc0055ceb87629f20993d74ad346d6e18d645 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 30 Nov 2022 11:42:09 +0100 Subject: [PATCH 054/426] [SonarCloud]: run only on org branch (#2772) --- .github/workflows/linux-ci-sonarcloud.yml | 71 ++++++++++++----------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/.github/workflows/linux-ci-sonarcloud.yml b/.github/workflows/linux-ci-sonarcloud.yml index fc69a791607..662c53dde90 100644 --- a/.github/workflows/linux-ci-sonarcloud.yml +++ b/.github/workflows/linux-ci-sonarcloud.yml @@ -8,42 +8,43 @@ on: jobs: build: + if: github.event.pull_request.head.repo.fork == false runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - name: Install system dependencies - run: | - # build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake - sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev --fix-missing - - name: Cache internal dependencies - id: internal_cache - uses: actions/cache@v1.1.2 - with: - path: build/local - key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} - - name: Install internal dependencies - run: | - tools/install-dependencies - env: - CC: /usr/bin/clang - CXX: /usr/bin/clang++ - if: steps.internal_cache.outputs.cache-hit != 'true' - - name: Code generation - run: | - tools/generate-files - env: - CC: /usr/bin/clang - CXX: /usr/bin/clang++ - - name: CMake (coverage/clang-tidy/clang-asan) - run: | - cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -DTW_CODE_COVERAGE=ON -DTW_ENABLE_CLANG_TIDY=ON -DTW_CLANG_ASAN=ON -GNinja - cat build/compile_commands.json - env: - CC: /usr/bin/clang - CXX: /usr/bin/clang++ - - name: SonarCloud Scan - run: | - ./tools/sonarcloud-analysis - env: + - uses: actions/checkout@v2 + - name: Install system dependencies + run: | + # build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake + sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev --fix-missing + - name: Cache internal dependencies + id: internal_cache + uses: actions/cache@v1.1.2 + with: + path: build/local + key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + - name: Install internal dependencies + run: | + tools/install-dependencies + env: + CC: /usr/bin/clang + CXX: /usr/bin/clang++ + if: steps.internal_cache.outputs.cache-hit != 'true' + - name: Code generation + run: | + tools/generate-files + env: + CC: /usr/bin/clang + CXX: /usr/bin/clang++ + - name: CMake (coverage/clang-tidy/clang-asan) + run: | + cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -DTW_CODE_COVERAGE=ON -DTW_ENABLE_CLANG_TIDY=ON -DTW_CLANG_ASAN=ON -GNinja + cat build/compile_commands.json + env: + CC: /usr/bin/clang + CXX: /usr/bin/clang++ + - name: SonarCloud Scan + run: | + ./tools/sonarcloud-analysis + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 2601c5fcb0d02db416ed77c13f89c3df588dc0f0 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 2 Dec 2022 18:27:24 +0100 Subject: [PATCH 055/426] [ThorSwap]: ERC20 token transfer (#2765) --- src/THORChain/Swap.cpp | 4 +- tests/chains/THORChain/SwapTests.cpp | 64 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index 3be886ab9a8..829f104ba69 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -195,7 +195,7 @@ SwapBundled SwapBuilder::buildBinance(Proto::Asset fromAsset, uint64_t amount, c SwapBundled SwapBuilder::buildEth(uint64_t amount, const std::string& memo) { Data out; auto input = Ethereum::Proto::SigningInput(); - const auto& toTokenId = mToAsset.token_id(); + const auto& toTokenId = mFromAsset.token_id(); // some sanity check / address conversion Data vaultAddressBin = ethAddressStringToData(mVaultAddress); if (!Ethereum::Address::isValid(mVaultAddress) || vaultAddressBin.size() != Ethereum::Address::size) { @@ -228,7 +228,7 @@ SwapBundled SwapBuilder::buildEth(uint64_t amount, const std::string& memo) { Data payload; func.encode(payload); transfer.set_data(payload.data(), payload.size()); - Data amountData = store(uint256_t(amount)); + Data amountData = store(toTokenId.empty() ? uint256_t(amount) : uint256_t(0)); transfer.set_amount(amountData.data(), amountData.size()); auto serialized = input.SerializeAsString(); diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index fb23e1f3a32..591ed6b9e07 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -191,6 +191,70 @@ Data SwapTest_ethAddressStringToData(const std::string& asString) { return asData; } +TEST(THORChainSwap, SwapErc20Rune) { + Proto::Asset fromAsset; + fromAsset.set_token_id("0xdAC17F958D2ee523a2206206994597C13D831ec7"); + fromAsset.set_chain(static_cast(Chain::ETH)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::THOR)); + toAsset.set_symbol("RUNE"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress("0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37") + .toAddress("thor1du84c7fj5y7kphq7zfyp8ugwxgrmy6n07xm9yj") + .vault("0x97673DF37E718dF203A834Bd095F69F6b4F314FA") + .router("0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146") + .fromAmount("5000000") + .toAmountLimit("418410520") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "0a01001201002201002a0100422a307844333742624535373434443733306131643938643844433937633432463043613436614437313436528d02328a020a01001284021fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a34313834313035323000000000000000000000000000000000000000000000000000000000000000"); + + auto tx = Ethereum::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); + + // check fields + EXPECT_EQ(tx.to_address(), "0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146"); + ASSERT_TRUE(tx.transaction().has_contract_generic()); + + Data vaultAddressBin = SwapTest_ethAddressStringToData("0x97673DF37E718dF203A834Bd095F69F6b4F314FA"); + EXPECT_EQ(hex(vaultAddressBin), "97673df37e718df203a834bd095f69f6b4f314fa"); + auto func = Ethereum::ABI::Function("deposit", std::vector>{ + std::make_shared(vaultAddressBin), + std::make_shared(parse_hex("0xdAC17F958D2ee523a2206206994597C13D831ec7")), + std::make_shared(uint256_t(5000000)), + std::make_shared("=:THOR.RUNE:thor1du84c7fj5y7kphq7zfyp8ugwxgrmy6n07xm9yj:418410520")}); + Data payload; + func.encode(payload); + EXPECT_EQ(hex(payload), "1fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a34313834313035323000000000000000000000000000000000000000000000000000000000000000"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "00"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); + + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set few fields before signing + auto chainId = store(uint256_t(1)); + tx.set_chain_id(chainId.data(), chainId.size()); + auto nonce = store(uint256_t(7)); + tx.set_nonce(nonce.data(), nonce.size()); + auto gasPrice = store(uint256_t(30000000000)); + tx.set_gas_price(gasPrice.data(), gasPrice.size()); + auto gasLimit = store(uint256_t(80000)); + tx.set_gas_limit(gasLimit.data(), gasLimit.size()); + auto privKey = parse_hex("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"); + tx.set_private_key(privKey.data(), privKey.size()); + + // sign and encode resulting input + Ethereum::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeEthereum); + EXPECT_EQ(hex(output.encoded()), "f9016b078506fc23ac008301388094d37bbe5744d730a1d98d8dc97c42f0ca46ad714680b901041fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a3431383431303532300000000000000000000000000000000000000000000000000000000000000026a03b9082870fda839820dd36d4da3d8985807c799a8cf8e1971374a461da5899a7a0383d9ceaacf6c90205d4381b403687c17c2bbfaac1c1329ac65c0ce22d940451"); + // https://viewblock.io/thorchain/tx/56D2A63608E6EC09FA1D2934457CC09196683013905F69EDFC72B33EC68681AA + // https://etherscan.io/tx/0x56d2a63608e6ec09fa1d2934457cc09196683013905f69edfc72b33ec68681aa + // https://viewblock.io/thorchain/tx/BC1464CF3B56B07E40CF57985511814AEC9EAE2F1329CEE059A21529FDDFDB8C +} + TEST(THORChainSwap, SwapEthBnb) { Proto::Asset fromAsset; fromAsset.set_chain(static_cast(Chain::ETH)); From 537c82e71c59b3ba9f0c2f788014a1cab42feea3 Mon Sep 17 00:00:00 2001 From: Tom Langer Date: Mon, 5 Dec 2022 03:19:46 +0200 Subject: [PATCH 056/426] Support for Secret Network (#2767) * update registry * CoinTypeTests * Update SignerTests.cpp * Update TWAnySignerTests.cpp * Fix tests to match the deterministic signature * fix signature test Co-authored-by: Cashmaney --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../blockchains/secret/TestSecretAddress.kt | 31 ++++++++ .../blockchains/secret/TestSecretSigner.kt | 77 +++++++++++++++++++ docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 32 ++++++++ swift/Tests/Blockchains/SecretTests.swift | 63 +++++++++++++++ swift/Tests/CoinAddressDerivationTests.swift | 3 + tests/chains/Secret/AddressTests.cpp | 47 +++++++++++ tests/chains/Secret/SignerTests.cpp | 59 ++++++++++++++ tests/chains/Secret/TWAnyAddressTests.cpp | 28 +++++++ tests/chains/Secret/TWAnySignerTests.cpp | 56 ++++++++++++++ tests/chains/Secret/TWCoinTypeTests.cpp | 37 +++++++++ tests/common/CoinAddressDerivationTests.cpp | 3 + tests/interface/TWHRPTests.cpp | 3 + 15 files changed, 442 insertions(+) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretSigner.kt create mode 100644 swift/Tests/Blockchains/SecretTests.swift create mode 100644 tests/chains/Secret/AddressTests.cpp create mode 100644 tests/chains/Secret/SignerTests.cpp create mode 100644 tests/chains/Secret/TWAnyAddressTests.cpp create mode 100644 tests/chains/Secret/TWAnySignerTests.cpp create mode 100644 tests/chains/Secret/TWCoinTypeTests.cpp 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 ddd2c1dd86c..8d60a752d44 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 @@ -107,5 +107,6 @@ class CoinAddressDerivationTests { EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) HEDERA -> assertEquals("0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5", address) + SECRET -> assertEquals("secret1f69sk5033zcdr2p2yf3xjehn7xvgdeq09d2llh", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretAddress.kt new file mode 100644 index 00000000000..26d66ee5883 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretAddress.kt @@ -0,0 +1,31 @@ +// Copyright © 2017-2022 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.secret + +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 TestSecretAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("87201512d132ef7a1e57f9e24905fbc24300bd73f676b5716182be5f3e39dada".toHexByteArray()) + val pubkey = key.getPublicKeySecp256k1(true) + val address = AnyAddress(pubkey, CoinType.SECRET) + val expected = AnyAddress("secret18mdrja40gfuftt5yx6tgj0fn5lurplezyp894y", CoinType.SECRET) + + assertEquals(pubkey.data().toHex(), "0x02466ac5d28cb4fab6c349060c6c1619e8d301e7741fb6b33cc1edac25f45d8646") + assertEquals(address.description(), expected.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretSigner.kt new file mode 100644 index 00000000000..2c25e3601da --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretSigner.kt @@ -0,0 +1,77 @@ +// Copyright © 2017-2022 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.secret + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexBytesInByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType.SECRET +import wallet.core.jni.proto.Cosmos +import wallet.core.jni.proto.Cosmos.SigningOutput +import wallet.core.jni.proto.Cosmos.SigningMode +import wallet.core.jni.* + +class TestSecretSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun SecretTransactionSigning() { + val key = PrivateKey("87201512d132ef7a1e57f9e24905fbc24300bd73f676b5716182be5f3e39dada".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(true) + val from = AnyAddress(publicKey, SECRET).description() + + val txAmount = Cosmos.Amount.newBuilder().apply { + amount = "100000" + denom = "uscrt" + }.build() + + val sendCoinsMsg = Cosmos.Message.Send.newBuilder().apply { + fromAddress = from + toAddress = "secret1rnq6hjfnalxeef87rmdeya3nu9dhpc7k9pujs3" + addAllAmounts(listOf(txAmount)) + }.build() + + val message = Cosmos.Message.newBuilder().apply { + sendCoinsMessage = sendCoinsMsg + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "2500" + denom = "uscrt" + }.build() + + val secretFee = Cosmos.Fee.newBuilder().apply { + gas = 25000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + accountNumber = 265538 + chainId = "secret-4" + memo = "" + sequence = 1 + fee = secretFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, SECRET, SigningOutput.parser()) + + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLXNlY3JldDE4bWRyamE0MGdmdWZ0dDV5eDZ0Z2owZm41bHVycGxlenlwODk0eRItc2VjcmV0MXJucTZoamZuYWx4ZWVmODdybWRleWEzbnU5ZGhwYzdrOXB1anMzGg8KBXVzY3J0EgYxMDAwMDASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAkZqxdKMtPq2w0kGDGwWGejTAed0H7azPMHtrCX0XYZGEgQKAggBGAESEwoNCgV1c2NydBIEMjUwMBCowwEaQOcHd2gHpa5WKZ/5RRerEtrHlyDlojIEzUGhC9xMFgs7UQMWy+kTTN+NRf7zQ8rx3cPkIKeZhv0u1KRc8uRCc4o=\"}") + assertEquals(output.error, "") + } +} diff --git a/docs/registry.md b/docs/registry.md index a1cecf40e66..b5ca89f1dbe 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -50,6 +50,7 @@ This list is generated from [./registry.json](../registry.json) | 500 | Theta | THETA | | | | 501 | Solana | SOL | | | | 508 | Elrond | eGLD | | | +| 529 | Secret | SCRT | | | | 637 | Aptos | APT | | | | 714 | BNB Beacon Chain | BNB | | | | 818 | VeChain | VET | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 9033f4ea806..aaf37364468 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -125,6 +125,7 @@ enum TWCoinType { TWCoinTypeEverscale = 396, TWCoinTypeAptos = 637, TWCoinTypeHedera = 3030, + TWCoinTypeSecret = 529, }; /// Returns the blockchain for a coin type. diff --git a/registry.json b/registry.json index d2dcc9d2b74..d2279f7144a 100644 --- a/registry.json +++ b/registry.json @@ -2344,6 +2344,38 @@ "clientDocs": "https://eth.wiki/json-rpc/API" } }, + { + "id": "secret", + "name": "Secret", + "displayName": "Secret", + "coinId": 529, + "symbol": "SCRT", + "decimals": 6, + "blockchain": "Cosmos", + "derivation": [ + { + "path": "m/44'/529'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "hrp": "secret", + "chainId": "secret-4", + "addressHasher": "sha256ripemd", + "explorer": { + "url": "https://mintscan.io/secret", + "txPath": "/txs/", + "accountPath": "/account/", + "sampleTx": "026B4886B1D9CE836A99755DDE99D4F8A7748D27B1CE9D298A763B1CFFF62C00", + "sampleAccount": "secret167m3s89ddurjpyr82vsluvvj0t8ylzn95trrqy" + }, + "info": { + "url": "https://scrt.network/", + "source": "https://github.com/scrtlabs/SecretNetwork", + "rpc": "https://scrt-rpc.blockpane.com/", + "documentation": "https://docs.scrt.network/" + } + }, { "id": "osmosis", "name": "Osmosis", diff --git a/swift/Tests/Blockchains/SecretTests.swift b/swift/Tests/Blockchains/SecretTests.swift new file mode 100644 index 00000000000..5abd8ff81b9 --- /dev/null +++ b/swift/Tests/Blockchains/SecretTests.swift @@ -0,0 +1,63 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class SecretTests: XCTestCase { + func testAddress() { + let key = PrivateKey(data: Data(hexString: "87201512d132ef7a1e57f9e24905fbc24300bd73f676b5716182be5f3e39dada")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: true) + let address = AnyAddress(publicKey: pubkey, coin: .secret) + let addressFromString = AnyAddress(string: "secret18mdrja40gfuftt5yx6tgj0fn5lurplezyp894y", coin: .secret)! + + XCTAssertEqual(pubkey.data.hexString, "02466ac5d28cb4fab6c349060c6c1619e8d301e7741fb6b33cc1edac25f45d8646") + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSigningTransaction() { + let privateKey = PrivateKey(data: Data(hexString: "87201512d132ef7a1e57f9e24905fbc24300bd73f676b5716182be5f3e39dada")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .secret) + + let sendCoinsMessage = CosmosMessage.Send.with { + $0.fromAddress = fromAddress.description + $0.toAddress = "secret1rnq6hjfnalxeef87rmdeya3nu9dhpc7k9pujs3" + $0.amounts = [CosmosAmount.with { + $0.amount = "100000" + $0.denom = "uscrt" + }] + } + + let message = CosmosMessage.with { + $0.sendCoinsMessage = sendCoinsMessage + } + + let fee = CosmosFee.with { + $0.gas = 25000 + $0.amounts = [CosmosAmount.with { + $0.amount = "2500" + $0.denom = "uscrt" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 265538 + $0.chainID = "secret-4" + $0.memo = "" + $0.sequence = 1 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .secret) + + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLXNlY3JldDE4bWRyamE0MGdmdWZ0dDV5eDZ0Z2owZm41bHVycGxlenlwODk0eRItc2VjcmV0MXJucTZoamZuYWx4ZWVmODdybWRleWEzbnU5ZGhwYzdrOXB1anMzGg8KBXVzY3J0EgYxMDAwMDASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAkZqxdKMtPq2w0kGDGwWGejTAed0H7azPMHtrCX0XYZGEgQKAggBGAESEwoNCgV1c2NydBIEMjUwMBCowwEaQOcHd2gHpa5WKZ/5RRerEtrHlyDlojIEzUGhC9xMFgs7UQMWy+kTTN+NRf7zQ8rx3cPkIKeZhv0u1KRc8uRCc4o=\"}") + XCTAssertEqual(output.error, "") + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 83311e02ca9..081a61cae55 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -259,6 +259,9 @@ class CoinAddressDerivationTests: XCTestCase { case .hedera: let expectedResult = "0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .secret: + let expectedResult = "secret1f69sk5033zcdr2p2yf3xjehn7xvgdeq09d2llh" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() diff --git a/tests/chains/Secret/AddressTests.cpp b/tests/chains/Secret/AddressTests.cpp new file mode 100644 index 00000000000..0295179e533 --- /dev/null +++ b/tests/chains/Secret/AddressTests.cpp @@ -0,0 +1,47 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "Cosmos/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include +#include + +namespace TW::Cosmos::tests { + +TEST(SecretAddress, Valid) { + ASSERT_TRUE(Address::isValid(TWCoinTypeSecret, "secret16vw3fp7x35tzmwlkdkyzr8vgscn0zewtduyjuf")); + ASSERT_TRUE(Address::isValid(TWCoinTypeSecret, "secret15rgv8teecnt53h0gdvngzt3am3yuz3rxh4fnle")); + ASSERT_TRUE(Address::isValid(TWCoinTypeSecret, "secret1pjp5dpvrjumn653dycszk3g264zgqusz2xhdq7")); +} + +TEST(SecretAddress, Invalid) { + ASSERT_FALSE(Address::isValid(TWCoinTypeSecret, "secret15rgv8teecnt53h0gdvngzt3am3yuz3rxh4fnla")); + ASSERT_FALSE(Address::isValid(TWCoinTypeSecret, "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")); // valid cosmos +} + +TEST(SecretAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("92ded9fb8b4646b66d0c127fa38523133ce11bdc5dadee2d5fc1c0ccd9fec664")); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Address(TWCoinTypeSecret, publicKey); + ASSERT_EQ(address.string(), "secret16vw3fp7x35tzmwlkdkyzr8vgscn0zewtduyjuf"); +} + +TEST(SecretAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("0250bba49f3d5a574b38de891387420877de10fd44de609567d5e6e0c97bfa4bb2"), TWPublicKeyTypeSECP256k1); + auto address = Address(TWCoinTypeSecret, publicKey); + ASSERT_EQ(address.string(), "secret16vw3fp7x35tzmwlkdkyzr8vgscn0zewtduyjuf"); +} + +TEST(SecretAddress, FromString) { + Address address; + EXPECT_TRUE(Address::decode("secret16vw3fp7x35tzmwlkdkyzr8vgscn0zewtduyjuf", address)); + EXPECT_EQ(address.string(), "secret16vw3fp7x35tzmwlkdkyzr8vgscn0zewtduyjuf"); + EXPECT_EQ(hex(address.getKeyHash()), "d31d1487c68d162dbbf66d88219d888626f165cb"); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Secret/SignerTests.cpp b/tests/chains/Secret/SignerTests.cpp new file mode 100644 index 00000000000..5bb99917be6 --- /dev/null +++ b/tests/chains/Secret/SignerTests.cpp @@ -0,0 +1,59 @@ +// Copyright © 2017-2022 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 "Cosmos/Address.h" +#include "Cosmos/Signer.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "TestUtilities.h" + +#include + +namespace TW::Cosmos::tests { + +TEST(SecretSigner, Sign) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(265538); + input.set_chain_id("secret-4"); + input.set_memo(""); + input.set_sequence(1); + + Address fromAddress; + Address toAddress; + EXPECT_TRUE(Address::decode("secret18mdrja40gfuftt5yx6tgj0fn5lurplezyp894y", fromAddress)); + EXPECT_TRUE(Address::decode("secret1rnq6hjfnalxeef87rmdeya3nu9dhpc7k9pujs3", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("uscrt"); + amountOfTx->set_amount("100000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(25000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uscrt"); + amountOfFee->set_amount("2500"); + + auto privateKey = parse_hex("87201512d132ef7a1e57f9e24905fbc24300bd73f676b5716182be5f3e39dada"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeSecret); + + // https://www.mintscan.io/secret/txs/01F4BD2458BF966F287533775C8D67BBC7CA7214CAEB1752D270A90223E9E82F + // curl -H 'Content-Type: application/json' --data-binary "{\"tx_bytes\":\"CpIB...c4o=\",\"mode\":\"BROADCAST_MODE_BLOCK\"}" https://scrt-lcd.blockpane.com/cosmos/tx/v1beta1/txs + + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLXNlY3JldDE4bWRyamE0MGdmdWZ0dDV5eDZ0Z2owZm41bHVycGxlenlwODk0eRItc2VjcmV0MXJucTZoamZuYWx4ZWVmODdybWRleWEzbnU5ZGhwYzdrOXB1anMzGg8KBXVzY3J0EgYxMDAwMDASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAkZqxdKMtPq2w0kGDGwWGejTAed0H7azPMHtrCX0XYZGEgQKAggBGAESEwoNCgV1c2NydBIEMjUwMBCowwEaQOcHd2gHpa5WKZ/5RRerEtrHlyDlojIEzUGhC9xMFgs7UQMWy+kTTN+NRf7zQ8rx3cPkIKeZhv0u1KRc8uRCc4o=\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "e707776807a5ae56299ff94517ab12dac79720e5a23204cd41a10bdc4c160b3b510316cbe9134cdf8d45fef343caf1ddc3e420a79986fd2ed4a45cf2e442738a"); + EXPECT_EQ(output.error(), ""); + EXPECT_EQ(output.json(), ""); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Secret/TWAnyAddressTests.cpp b/tests/chains/Secret/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..073fb314437 --- /dev/null +++ b/tests/chains/Secret/TWAnyAddressTests.cpp @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 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 +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TWSecretAnyAddress, IsValid) { + EXPECT_TRUE(TWAnyAddressIsValid(STRING("secret16vw3fp7x35tzmwlkdkyzr8vgscn0zewtduyjuf").get(), TWCoinTypeSecret)); + EXPECT_TRUE(TWAnyAddressIsValid(STRING("secret15rgv8teecnt53h0gdvngzt3am3yuz3rxh4fnle").get(), TWCoinTypeSecret)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02").get(), TWCoinTypeSecret)); +} + +TEST(TWSecretAnyAddress, Create) { + auto string = STRING("secret16vw3fp7x35tzmwlkdkyzr8vgscn0zewtduyjuf"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeSecret)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "d31d1487c68d162dbbf66d88219d888626f165cb"); +} diff --git a/tests/chains/Secret/TWAnySignerTests.cpp b/tests/chains/Secret/TWAnySignerTests.cpp new file mode 100644 index 00000000000..26063c58b5a --- /dev/null +++ b/tests/chains/Secret/TWAnySignerTests.cpp @@ -0,0 +1,56 @@ +// Copyright © 2017-2022 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 "Cosmos/Address.h" +#include "HexCoding.h" +#include "proto/Cosmos.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Cosmos::tests { + +TEST(TWAnySignerSecret, Sign) { + auto privateKey = parse_hex("87201512d132ef7a1e57f9e24905fbc24300bd73f676b5716182be5f3e39dada"); + Proto::SigningInput input; + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(265538); + input.set_chain_id("secret-4"); + input.set_memo(""); + input.set_sequence(1); + input.set_private_key(privateKey.data(), privateKey.size()); + + Address fromAddress; + Address toAddress; + EXPECT_TRUE(Address::decode("secret18mdrja40gfuftt5yx6tgj0fn5lurplezyp894y", fromAddress)); + EXPECT_TRUE(Address::decode("secret1rnq6hjfnalxeef87rmdeya3nu9dhpc7k9pujs3", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("uscrt"); + amountOfTx->set_amount("100000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(25000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uscrt"); + amountOfFee->set_amount("2500"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSecret); + + // https://www.mintscan.io/secret/txs/01F4BD2458BF966F287533775C8D67BBC7CA7214CAEB1752D270A90223E9E82F + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLXNlY3JldDE4bWRyamE0MGdmdWZ0dDV5eDZ0Z2owZm41bHVycGxlenlwODk0eRItc2VjcmV0MXJucTZoamZuYWx4ZWVmODdybWRleWEzbnU5ZGhwYzdrOXB1anMzGg8KBXVzY3J0EgYxMDAwMDASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAkZqxdKMtPq2w0kGDGwWGejTAed0H7azPMHtrCX0XYZGEgQKAggBGAESEwoNCgV1c2NydBIEMjUwMBCowwEaQOcHd2gHpa5WKZ/5RRerEtrHlyDlojIEzUGhC9xMFgs7UQMWy+kTTN+NRf7zQ8rx3cPkIKeZhv0u1KRc8uRCc4o=\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "e707776807a5ae56299ff94517ab12dac79720e5a23204cd41a10bdc4c160b3b510316cbe9134cdf8d45fef343caf1ddc3e420a79986fd2ed4a45cf2e442738a"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Secret/TWCoinTypeTests.cpp b/tests/chains/Secret/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..62401fe8656 --- /dev/null +++ b/tests/chains/Secret/TWCoinTypeTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 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. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWSecretCoinType, TWCoinType) { + const auto coin = TWCoinTypeSecret; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("026B4886B1D9CE836A99755DDE99D4F8A7748D27B1CE9D298A763B1CFFF62C00")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("secret167m3s89ddurjpyr82vsluvvj0t8ylzn95trrqy")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "secret"); + assertStringsEqual(name, "Secret"); + assertStringsEqual(symbol, "SCRT"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 6); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "secret-4"); + assertStringsEqual(txUrl, "https://mintscan.io/secret/txs/026B4886B1D9CE836A99755DDE99D4F8A7748D27B1CE9D298A763B1CFFF62C00"); + assertStringsEqual(accUrl, "https://mintscan.io/secret/account/secret167m3s89ddurjpyr82vsluvvj0t8ylzn95trrqy"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index eacfaa549e9..be0e69bce0a 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -255,6 +255,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeHedera: EXPECT_EQ(address, "0.0.302a300506032b6570032100ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148"); break; + case TWCoinTypeSecret: + EXPECT_EQ(address, "secret1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0m7t23a"); + break; // no default branch here, intentionally, to better notice any missing coins } } diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index a24bda956ff..22195dcd3e0 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -35,6 +35,7 @@ TEST(TWHRP, StringForHRP) { ASSERT_STREQ(stringForHRP(TWHRPTHORChain), "thor"); ASSERT_STREQ(stringForHRP(TWHRPCryptoOrg), "cro"); ASSERT_STREQ(stringForHRP(TWHRPOsmosis), "osmo"); + ASSERT_STREQ(stringForHRP(TWHRPSecret), "secret"); } TEST(TWHRP, HRPForString) { @@ -62,6 +63,7 @@ TEST(TWHRP, HRPForString) { ASSERT_EQ(hrpForString("cro"), TWHRPCryptoOrg); ASSERT_EQ(hrpForString("osmo"), TWHRPOsmosis); ASSERT_EQ(hrpForString("ecash"), TWHRPECash); + ASSERT_EQ(hrpForString("secret"), TWHRPSecret); } TEST(TWHPR, HPRByCoinType) { @@ -88,6 +90,7 @@ TEST(TWHPR, HPRByCoinType) { ASSERT_EQ(TWHRPCryptoOrg, TWCoinTypeHRP(TWCoinTypeCryptoOrg)); ASSERT_EQ(TWHRPOsmosis, TWCoinTypeHRP(TWCoinTypeOsmosis)); ASSERT_EQ(TWHRPECash, TWCoinTypeHRP(TWCoinTypeECash)); + ASSERT_EQ(TWHRPSecret, TWCoinTypeHRP(TWCoinTypeSecret)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeAion)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeCallisto)); From f77d528d17020c97b15f59d9578776d3899220b5 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 5 Dec 2022 13:14:56 +0100 Subject: [PATCH 057/426] [Bindgen]: Add rust wallet core rust library + Aptos blind signing (#2706) --- .github/workflows/android-ci.yml | 4 +- .github/workflows/ios-ci.yml | 3 +- .github/workflows/linux-ci.yml | 4 +- .github/workflows/linux-sampleapp-ci.yml | 3 +- .github/workflows/wasm-ci.yml | 5 +- CMakeLists.txt | 19 +- .../app/blockchains/aptos/TestAptosSigner.kt | 48 + rust/.gitignore | 7 + rust/Cargo.lock | 224 ++++ rust/Cargo.toml | 17 + rust/src/lib.rs | 8 + rust/src/memory/mod.rs | 30 + rust/src/move_parser/mod.rs | 87 ++ samples/cpp/CMakeLists.txt | 2 +- samples/go/core/bitcoin.go | 2 +- samples/go/core/coin.go | 2 +- samples/go/core/datavector.go | 2 +- samples/go/core/mnemonic.go | 2 +- samples/go/core/publicKey.go | 2 +- samples/go/core/transaction.go | 2 +- samples/go/core/transactionHelper.go | 2 +- samples/go/core/wallet.go | 2 +- samples/go/dev-console/native/cgo.go | 2 +- samples/go/dev-console/prepare.sh | 3 +- samples/go/protos/binance/Binance.pb.go | 990 +++++++++--------- samples/go/protos/bitcoin/Bitcoin.pb.go | 313 ++---- samples/go/protos/common/Common.pb.go | 147 +-- samples/go/protos/ethereum/Ethereum.pb.go | 77 +- .../TransactionCompiler.pb.go | 24 +- samples/go/sample/external_signing.go | 14 +- samples/go/types/twdata.go | 2 +- samples/go/types/twstring.go | 2 +- samples/rust/src/build.rs | 3 +- src/Aptos/Signer.cpp | 49 +- src/Aptos/TransactionPayload.cpp | 62 ++ src/Aptos/TransactionPayload.h | 1 + src/rust/bindgen/.gitignore | 1 + swift/.gitignore | 1 + swift/Tests/Blockchains/AptosTests.swift | 36 + swift/common-xcframework.yml | 4 +- swift/fastlane/Fastfile | 6 +- swift/project.yml | 4 +- tests/chains/Aptos/SignerTests.cpp | 63 ++ .../common/rust/bindgen/WalletCoreRsTests.cpp | 16 + tools/generate-files | 4 + tools/install-rust-dependencies | 24 + tools/install-sys-dependencies-linux | 6 + tools/install-sys-dependencies-mac | 5 + tools/ios-build | 3 +- tools/ios-doc | 2 +- tools/rust-bindgen | 43 + 51 files changed, 1529 insertions(+), 855 deletions(-) create mode 100644 rust/.gitignore create mode 100644 rust/Cargo.lock create mode 100644 rust/Cargo.toml create mode 100644 rust/src/lib.rs create mode 100644 rust/src/memory/mod.rs create mode 100644 rust/src/move_parser/mod.rs rename samples/go/protos/{common => transactioncompiler}/TransactionCompiler.pb.go (87%) create mode 100644 src/rust/bindgen/.gitignore create mode 100644 tests/common/rust/bindgen/WalletCoreRsTests.cpp create mode 100755 tools/install-rust-dependencies create mode 100755 tools/install-sys-dependencies-linux create mode 100755 tools/install-sys-dependencies-mac create mode 100755 tools/rust-bindgen diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 3dfe3e5b9f0..5203adc04dc 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -19,7 +19,9 @@ jobs: java-version: 11 - name: Install system dependencies - run: brew install boost ninja + run: | + tools/install-sys-dependencies-mac + tools/install-rust-dependencies - name: Install Android Dependencies run: tools/install-android-dependencies diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index a7078f649e3..61813885a4d 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -13,7 +13,8 @@ jobs: - uses: actions/checkout@v2 - name: Install system dependencies run: | - brew install boost ninja xcodegen xcbeautify + tools/install-sys-dependencies-mac + tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache uses: actions/cache@v1.1.2 diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index bed1b725444..5bef8727e51 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -13,8 +13,8 @@ jobs: - uses: actions/checkout@v2 - name: Install system dependencies run: | - # build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake - sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev --fix-missing + tools/install-sys-dependencies-linux + tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache uses: actions/cache@v1.1.2 diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index 67bbec223ee..7ed0fdcbbb0 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -13,7 +13,8 @@ jobs: - uses: actions/checkout@v2 - name: Install system dependencies run: | - sudo apt-get update && sudo apt-get install ninja-build llvm-11 libboost-all-dev clang-11 rustc --fix-missing + tools/install-sys-dependencies-linux + tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache uses: actions/cache@v1.1.2 diff --git a/.github/workflows/wasm-ci.yml b/.github/workflows/wasm-ci.yml index d383cc49195..d41158c4933 100644 --- a/.github/workflows/wasm-ci.yml +++ b/.github/workflows/wasm-ci.yml @@ -14,9 +14,8 @@ jobs: - name: Install system dependencies run: | - # build-essential clang-11 libc++-dev libc++abi-dev ruby-full cmake python3 - sudo apt-get update && sudo apt-get install libboost-all-dev --fix-missing - + tools/install-sys-dependencies-linux + tools/install-rust-dependencies - name: Install emsdk run: tools/install-wasm-dependencies diff --git a/CMakeLists.txt b/CMakeLists.txt index ed02ed918ed..a6dbdf3c1f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,8 +30,11 @@ target_link_directories(${PROJECT_NAME}_INTERFACE INTERFACE ${PREFIX}/lib) set_project_warnings(${PROJECT_NAME}_INTERFACE) add_subdirectory(trezor-crypto) +set(WALLET_CORE_RS_LIB libwallet_core_rs.a) +set(WALLET_CORE_BINDGEN ${PREFIX}/lib/${WALLET_CORE_RS_LIB}) if (TW_COMPILE_WASM) message(STATUS "Wasm build enabled") + set(WALLET_CORE_BINDGEN ${PREFIX}/wasm32-unknown-emscripten/release/${WALLET_CORE_RS_LIB}) add_subdirectory(wasm) endif () @@ -48,13 +51,21 @@ if (${ANDROID}) file(GLOB_RECURSE sources src/*.c src/*.cc src/*.cpp src/*.h jni/cpp/*.c jni/cpp/*.cpp jni/cpp/*.h jni/cpp/*.c) add_library(TrustWalletCore SHARED ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) find_library(log-lib log) - target_link_libraries(TrustWalletCore PUBLIC ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto protobuf ${log-lib} Boost::boost) + if (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a") + set(WALLET_CORE_BINDGEN ${PREFIX}/aarch64-linux-android/release/${WALLET_CORE_RS_LIB}) + elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86") + set(WALLET_CORE_BINDGEN ${PREFIX}/i686-linux-android/release/${WALLET_CORE_RS_LIB}) + elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a") + set(WALLET_CORE_BINDGEN ${PREFIX}/armv7-linux-androideabi/release/${WALLET_CORE_RS_LIB}) + elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86_64") + set(WALLET_CORE_BINDGEN ${PREFIX}/x86_64-linux-android/release/${WALLET_CORE_RS_LIB}) + endif() + target_link_libraries(TrustWalletCore PUBLIC ${WALLET_CORE_BINDGEN} ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto protobuf ${log-lib} Boost::boost) else () message("Configuring standalone") file(GLOB_RECURSE sources src/*.c src/*.cc src/*.cpp src/*.h) - add_library(TrustWalletCore ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) - - target_link_libraries(TrustWalletCore PUBLIC ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto protobuf Boost::boost) + add_library(TrustWalletCore STATIC ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) + target_link_libraries(TrustWalletCore PUBLIC ${WALLET_CORE_BINDGEN} ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto protobuf Boost::boost) endif () if (TW_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt index 6d52a4c9d1d..2b9d9b416c1 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt @@ -23,6 +23,54 @@ class TestAptosSigner { System.loadLibrary("TrustWalletCore") } + @Test + fun AptosTransactionBlindSigning() { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x7efd69e7f9462774b932ce500ab51c0d0dcc004cf272e09f8ffd5804c2a84e33?network=mainnet + val key = + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec".toHexBytesInByteString() + + val payloadJson = """ + { + "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", + "type_arguments": [ + "0x1::aptos_coin::AptosCoin", + "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" + ], + "arguments": [ + "1000000", + "49329" + ], + "type": "entry_function_payload" + } + """.trimIndent() + val signingInput = Aptos.SigningInput.newBuilder() + .setChainId(1) + .setExpirationTimestampSecs(3664390082) + .setGasUnitPrice(100) + .setMaxGasAmount(100011) + .setSender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30") + .setSequenceNumber(42) + .setAnyEncoded(payloadJson) + .setPrivateKey(key) + .build() + + val result = AnySigner.sign(signingInput, CoinType.APTOS, Aptos.SigningOutput.parser()) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.rawTxn.toByteArray())), + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada0000000001" + ) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.authenticator.signature.toByteArray())), + "42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b" + ) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.encoded.toByteArray())), + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c4042cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b" + ) + } + @Test fun AptosTransactionSigning() { // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 00000000000..bca6f9b85aa --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 00000000000..18607412aba --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,224 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + +[[package]] +name = "bcs" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b06b4c1f053002b70e7084ac944c77d58d5d92b2110dbc5e852735e00ad3ccc" +dependencies = [ + "serde", + "thiserror", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "move-core-types" +version = "0.0.4" +source = "git+https://github.com/move-language/move?rev=f7137eabc2046f76fdad3ded2c51e03a3b1fbd01#f7137eabc2046f76fdad3ded2c51e03a3b1fbd01" +dependencies = [ + "anyhow", + "bcs", + "hex", + "once_cell", + "rand", + "ref-cast", + "serde", + "serde_bytes", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b15debb4f9d60d767cd8ca9ef7abb2452922f3214671ff052defc7f3502c44" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfa8511e9e94fd3de6585a3d3cd00e01ed556dc9814829280af0e8dc72a8f36" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "wallet-core-rs" +version = "0.1.0" +dependencies = [ + "bcs", + "hex", + "move-core-types", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 00000000000..9a75f71be9c --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "wallet-core-rs" +version = "0.1.0" +edition = "2021" + +[lib] +name = "wallet_core_rs" +crate-type = ["staticlib"] # Creates static lib + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +move-core-types = { git = "https://github.com/move-language/move", rev = "f7137eabc2046f76fdad3ded2c51e03a3b1fbd01", features = ["address32"] } +bcs = "0.1.4" +hex = "0.4.3" + +[dev-dependencies] diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 00000000000..4f3c0712189 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2022 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. + +pub mod move_parser; +pub mod memory; diff --git a/rust/src/memory/mod.rs b/rust/src/memory/mod.rs new file mode 100644 index 00000000000..91bb4d4ddf6 --- /dev/null +++ b/rust/src/memory/mod.rs @@ -0,0 +1,30 @@ +// Copyright © 2017-2022 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. + +use std::ffi::{c_char, CString}; + +#[no_mangle] +pub unsafe extern fn free_string(ptr: *const c_char) { + // Take the ownership back to rust and drop the owner + let _ = CString::from_raw(ptr as *mut _); +} + +pub fn c_string_standalone(input: String) -> *const c_char { + let res = CString::new(input).unwrap(); + let p = res.as_ptr(); + std::mem::forget(res); + p +} + +#[cfg(test)] +mod tests { + use crate::memory::{c_string_standalone, free_string}; + + #[test] + fn tests_ffi_string() { + unsafe { free_string(c_string_standalone("foo".to_string())); } + } +} diff --git a/rust/src/move_parser/mod.rs b/rust/src/move_parser/mod.rs new file mode 100644 index 00000000000..ba788b5b042 --- /dev/null +++ b/rust/src/move_parser/mod.rs @@ -0,0 +1,87 @@ +// Copyright © 2017-2022 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. + +use std::{ + ffi::c_char, + ffi::CStr +}; +use move_core_types::*; +use move_core_types::language_storage::TypeTag; +use move_core_types::transaction_argument::TransactionArgument; +use crate::memory; + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub enum ETypeTag { + Bool = 1, + U8 = 2, + U64 = 3, + U128 = 4, + Address = 5, + Signer = 6, + Vector = 7, + Struct = 8, + Error = 9 +} + +#[no_mangle] +pub extern fn parse_type_tag(input: *const c_char) -> ETypeTag { + let s = unsafe { CStr::from_ptr(input).to_str().unwrap() }; + let transaction_argument = match parser::parse_type_tag(s) { + Ok(v) => v, + Err(_) => return ETypeTag::Error + }; + return match transaction_argument { + TypeTag::Bool => ETypeTag::Bool, + TypeTag::U8 => ETypeTag::U8, + TypeTag::U64 => ETypeTag::U64, + TypeTag::U128 => ETypeTag::U128, + TypeTag::Address => ETypeTag::Address, + TypeTag::Signer => ETypeTag::Signer, + TypeTag::Vector(_) => ETypeTag::Vector, + TypeTag::Struct(_) => ETypeTag::Struct + } +} + +#[no_mangle] +pub extern fn parse_function_argument_to_bcs(input: *const c_char) -> *const c_char { + let s = unsafe { CStr::from_ptr(input).to_str().unwrap() }; + let transaction_argument = match parser::parse_transaction_argument(s) { + Ok(v) => v, + Err(_) => return "\0".as_ptr() as *const c_char + }; + let v = match transaction_argument { + TransactionArgument::U8(v) => hex::encode(bcs::to_bytes(&v).unwrap()), + TransactionArgument::U64(v) => hex::encode(bcs::to_bytes(&v).unwrap()), + TransactionArgument::U128(v) => hex::encode(bcs::to_bytes(&v).unwrap()), + TransactionArgument::Address(v) => hex::encode(bcs::to_bytes(&v).unwrap()), + TransactionArgument::U8Vector(v) => hex::encode(bcs::to_bytes(&v).unwrap()), + TransactionArgument::Bool(v) => hex::encode(bcs::to_bytes(&v).unwrap()), + }; + memory::c_string_standalone(v) +} + +#[cfg(test)] +mod tests { + use crate::move_parser::{ETypeTag, parse_function_argument_to_bcs, parse_type_tag}; + use std::ffi::CStr; + use std::ffi::c_char; + + #[test] + fn tests_type_tag() { + let tag = parse_type_tag("0x1::aptos_coin::AptosCoin\0".as_ptr() as *const c_char); + assert_eq!(tag, ETypeTag::Struct); + } + + #[test] + fn tests_function_argument_to_bcs() { + let str = unsafe { CStr::from_ptr(parse_function_argument_to_bcs("10000000\0".as_ptr() as *const c_char)).to_str().unwrap() }; + assert_eq!(str, "8096980000000000"); + + let str = unsafe { CStr::from_ptr(parse_function_argument_to_bcs("5047445908\0".as_ptr() as *const c_char)).to_str().unwrap() }; + assert_eq!(str, "94e9d92c01000000"); + } +} diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index 5ecf5e6ba0d..a7c743f3e81 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -71,4 +71,4 @@ SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINK_FLAGS}") add_executable (sample sample.cpp) # link with our library, and default platform libraries -target_link_libraries (sample TrustWalletCore TrezorCrypto protobuf pthread ${PLATFORM_LIBS}) +target_link_libraries (sample PUBLIC TrustWalletCore wallet_core_rs TrezorCrypto protobuf pthread ${PLATFORM_LIBS}) diff --git a/samples/go/core/bitcoin.go b/samples/go/core/bitcoin.go index 5613b452ffa..b9b0d218298 100644 --- a/samples/go/core/bitcoin.go +++ b/samples/go/core/bitcoin.go @@ -1,7 +1,7 @@ package core // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include // #include // #include diff --git a/samples/go/core/coin.go b/samples/go/core/coin.go index 122e9a286fb..f1dd5dc94d9 100644 --- a/samples/go/core/coin.go +++ b/samples/go/core/coin.go @@ -1,7 +1,7 @@ package core // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include // #include import "C" diff --git a/samples/go/core/datavector.go b/samples/go/core/datavector.go index 014030e8817..04a4e4bd520 100644 --- a/samples/go/core/datavector.go +++ b/samples/go/core/datavector.go @@ -1,7 +1,7 @@ package core // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include import "C" import "tw/types" diff --git a/samples/go/core/mnemonic.go b/samples/go/core/mnemonic.go index 12394cc86b9..a1968b864b9 100644 --- a/samples/go/core/mnemonic.go +++ b/samples/go/core/mnemonic.go @@ -1,7 +1,7 @@ package core // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include import "C" diff --git a/samples/go/core/publicKey.go b/samples/go/core/publicKey.go index d116f8e7270..bf70a64622f 100644 --- a/samples/go/core/publicKey.go +++ b/samples/go/core/publicKey.go @@ -1,7 +1,7 @@ package core // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include import "C" diff --git a/samples/go/core/transaction.go b/samples/go/core/transaction.go index 3b3333d5a11..8aa9e1a900c 100644 --- a/samples/go/core/transaction.go +++ b/samples/go/core/transaction.go @@ -1,7 +1,7 @@ package core // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include // #include import "C" diff --git a/samples/go/core/transactionHelper.go b/samples/go/core/transactionHelper.go index c8414724a32..6062be8b753 100644 --- a/samples/go/core/transactionHelper.go +++ b/samples/go/core/transactionHelper.go @@ -1,7 +1,7 @@ package core // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include import "C" import "tw/types" diff --git a/samples/go/core/wallet.go b/samples/go/core/wallet.go index 09af05cdd42..ee95b763631 100644 --- a/samples/go/core/wallet.go +++ b/samples/go/core/wallet.go @@ -1,7 +1,7 @@ package core // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include // #include // #include diff --git a/samples/go/dev-console/native/cgo.go b/samples/go/dev-console/native/cgo.go index 3e0569e9162..b393d457f7a 100644 --- a/samples/go/dev-console/native/cgo.go +++ b/samples/go/dev-console/native/cgo.go @@ -1,7 +1,7 @@ package native // #cgo CFLAGS: -I packaged/include -// #cgo LDFLAGS: -lTrustWalletCore -lstdc++ -lm -lprotobuf -lTrezorCrypto +// #cgo LDFLAGS: -lTrustWalletCore -lstdc++ -lm -lprotobuf -lwallet_core_rs -lTrezorCrypto // // // #cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib -L${SRCDIR}/packaged/lib diff --git a/samples/go/dev-console/prepare.sh b/samples/go/dev-console/prepare.sh index 76ec7e9bb85..ff2a4f6af3a 100755 --- a/samples/go/dev-console/prepare.sh +++ b/samples/go/dev-console/prepare.sh @@ -5,6 +5,7 @@ cmake -DCMAKE_BUILD_TYPE=Release -DTW_UNIT_TESTS=OFF -DTW_BUILD_EXAMPLES=OFF -DT ninja cp libTrustWalletCore.a ../native/packaged/lib/ cp libprotobuf.a ../native/packaged/lib +cp ../../../../build/local/lib/libwallet_core_rs.a ../native/packaged/lib cp trezor-crypto/libTrezorCrypto.a ../native/packaged/lib cd - -cp -R ../../../include native/packaged/ \ No newline at end of file +cp -R ../../../include native/packaged/ diff --git a/samples/go/protos/binance/Binance.pb.go b/samples/go/protos/binance/Binance.pb.go index 9205f012247..9f8752135da 100644 --- a/samples/go/protos/binance/Binance.pb.go +++ b/samples/go/protos/binance/Binance.pb.go @@ -1,17 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.19.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.5 // source: Binance.proto package binance import ( - common "tw/protos/common" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + common "tw/protos/common" ) const ( @@ -21,18 +21,22 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Transaction structure, used internally type Transaction struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // int64 SIZE-OF-ENCODED // varint encoded length of the structure after encoding - // 0xF0625DEE // prefix - Msgs [][]byte `protobuf:"bytes,1,rep,name=msgs,proto3" json:"msgs,omitempty"` // array of size 1, containing the transaction message, which are one of the transaction type below - Signatures [][]byte `protobuf:"bytes,2,rep,name=signatures,proto3" json:"signatures,omitempty"` // array of size 1, containing the standard signature structure of the transaction sender - Memo string `protobuf:"bytes,3,opt,name=memo,proto3" json:"memo,omitempty"` // a short sentence of remark for the transaction, only for `Transfer` transactions. - Source int64 `protobuf:"varint,4,opt,name=source,proto3" json:"source,omitempty"` // an identifier for tools triggerring this transaction, set to zero if unwilling to disclose. - Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` // reserved for future use + // array of size 1, containing the transaction message, which are one of the transaction type below + Msgs [][]byte `protobuf:"bytes,1,rep,name=msgs,proto3" json:"msgs,omitempty"` + // array of size 1, containing the standard signature structure of the transaction sender + Signatures [][]byte `protobuf:"bytes,2,rep,name=signatures,proto3" json:"signatures,omitempty"` + // a short sentence of remark for the transaction, only for `Transfer` transactions. + Memo string `protobuf:"bytes,3,opt,name=memo,proto3" json:"memo,omitempty"` + // an identifier for tools triggering this transaction, set to zero if unwilling to disclose. + Source int64 `protobuf:"varint,4,opt,name=source,proto3" json:"source,omitempty"` + // reserved for future use + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` } func (x *Transaction) Reset() { @@ -102,15 +106,20 @@ func (x *Transaction) GetData() []byte { return nil } +// Signature structure, used internally type Signature struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PubKey []byte `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` // public key bytes of the signer address - Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` // signature bytes, please check chain access section for signature generation - AccountNumber int64 `protobuf:"varint,3,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` // another identifier of signer, which can be read from chain by account REST API or RPC - Sequence int64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` // sequence number for the next transaction + // public key bytes of the signer address + PubKey []byte `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` + // signature bytes, please check chain access section for signature generation + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + // another identifier of signer, which can be read from chain by account REST API or RPC + AccountNumber int64 `protobuf:"varint,3,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + // sequence number for the next transaction + Sequence int64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` } func (x *Signature) Reset() { @@ -173,20 +182,28 @@ func (x *Signature) GetSequence() int64 { return 0 } +// Message for Trade order type TradeOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0xCE6DC043 // prefix - Sender []byte `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` // originating address - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` // order id, optional - Symbol string `protobuf:"bytes,3,opt,name=symbol,proto3" json:"symbol,omitempty"` // symbol for trading pair in full name of the tokens - Ordertype int64 `protobuf:"varint,4,opt,name=ordertype,proto3" json:"ordertype,omitempty"` // only accept 2 for now, meaning limit order - Side int64 `protobuf:"varint,5,opt,name=side,proto3" json:"side,omitempty"` // 1 for buy and 2 fory sell - Price int64 `protobuf:"varint,6,opt,name=price,proto3" json:"price,omitempty"` // price of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer - Quantity int64 `protobuf:"varint,7,opt,name=quantity,proto3" json:"quantity,omitempty"` // quantity of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer - Timeinforce int64 `protobuf:"varint,8,opt,name=timeinforce,proto3" json:"timeinforce,omitempty"` // 1 for Good Till Expire(GTE) order and 3 for Immediate Or Cancel (IOC) + // originating address + Sender []byte `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // order id, optional + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + // symbol for trading pair in full name of the tokens + Symbol string `protobuf:"bytes,3,opt,name=symbol,proto3" json:"symbol,omitempty"` + // only accept 2 for now, meaning limit order + Ordertype int64 `protobuf:"varint,4,opt,name=ordertype,proto3" json:"ordertype,omitempty"` + // 1 for buy and 2 for sell + Side int64 `protobuf:"varint,5,opt,name=side,proto3" json:"side,omitempty"` + // price of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer + Price int64 `protobuf:"varint,6,opt,name=price,proto3" json:"price,omitempty"` + // quantity of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer + Quantity int64 `protobuf:"varint,7,opt,name=quantity,proto3" json:"quantity,omitempty"` + // 1 for Good Till Expire(GTE) order and 3 for Immediate Or Cancel (IOC) + Timeinforce int64 `protobuf:"varint,8,opt,name=timeinforce,proto3" json:"timeinforce,omitempty"` } func (x *TradeOrder) Reset() { @@ -277,15 +294,18 @@ func (x *TradeOrder) GetTimeinforce() int64 { return 0 } +// Message for CancelTrade order type CancelTradeOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0x166E681B // prefix - Sender []byte `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` // originating address - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // symbol for trading pair in full name of the tokens - Refid string `protobuf:"bytes,3,opt,name=refid,proto3" json:"refid,omitempty"` // order id to cancel + // originating address + Sender []byte `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // symbol for trading pair in full name of the tokens + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` + // order id to cancel + Refid string `protobuf:"bytes,3,opt,name=refid,proto3" json:"refid,omitempty"` } func (x *CancelTradeOrder) Reset() { @@ -341,12 +361,15 @@ func (x *CancelTradeOrder) GetRefid() string { return "" } +// Message for Send order type SendOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Inputs []*SendOrder_Input `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"` + // Send inputs + Inputs []*SendOrder_Input `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"` + // Send outputs Outputs []*SendOrder_Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"` } @@ -396,17 +419,22 @@ func (x *SendOrder) GetOutputs() []*SendOrder_Output { return nil } +// Message for TokenIssue order type TokenIssueOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0x17EFAB80 // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // token name - Symbol string `protobuf:"bytes,3,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix - TotalSupply int64 `protobuf:"varint,4,opt,name=total_supply,json=totalSupply,proto3" json:"total_supply,omitempty"` // total supply - Mintable bool `protobuf:"varint,5,opt,name=mintable,proto3" json:"mintable,omitempty"` // mintable + // owner address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // token name + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // token symbol, in full name with "-" suffix + Symbol string `protobuf:"bytes,3,opt,name=symbol,proto3" json:"symbol,omitempty"` + // total supply + TotalSupply int64 `protobuf:"varint,4,opt,name=total_supply,json=totalSupply,proto3" json:"total_supply,omitempty"` + // mintable + Mintable bool `protobuf:"varint,5,opt,name=mintable,proto3" json:"mintable,omitempty"` } func (x *TokenIssueOrder) Reset() { @@ -476,15 +504,18 @@ func (x *TokenIssueOrder) GetMintable() bool { return false } +// Message for TokenMint order type TokenMintOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0x467E0829 // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix - Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // amount to mint + // owner address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // token symbol, in full name with "-" suffix + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` + // amount to mint + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` } func (x *TokenMintOrder) Reset() { @@ -540,15 +571,18 @@ func (x *TokenMintOrder) GetAmount() int64 { return 0 } +// Message for TokenBurn order type TokenBurnOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0x7ED2D2A0 // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix - Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // amount to burn + // owner address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // token symbol, in full name with "-" suffix + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` + // amount to burn + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` } func (x *TokenBurnOrder) Reset() { @@ -604,15 +638,18 @@ func (x *TokenBurnOrder) GetAmount() int64 { return 0 } +// Message for TokenFreeze order type TokenFreezeOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0xE774B32D // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix - Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // amount of token to freeze + // owner address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // token symbol, in full name with "-" suffix + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` + // amount of token to freeze + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` } func (x *TokenFreezeOrder) Reset() { @@ -668,15 +705,18 @@ func (x *TokenFreezeOrder) GetAmount() int64 { return 0 } +// Message for TokenUnfreeze order type TokenUnfreezeOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0x6515FF0D // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix - Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // amount of token to unfreeze + // owner address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // token symbol, in full name with "-" suffix + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` + // amount of token to unfreeze + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` } func (x *TokenUnfreezeOrder) Reset() { @@ -732,22 +772,32 @@ func (x *TokenUnfreezeOrder) GetAmount() int64 { return 0 } +// Message for HashTimeLock order type HTLTOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0xB33F9A24 // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // signer address - To []byte `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` // recipient address - RecipientOtherChain string `protobuf:"bytes,3,opt,name=recipient_other_chain,json=recipientOtherChain,proto3" json:"recipient_other_chain,omitempty"` - SenderOtherChain string `protobuf:"bytes,4,opt,name=sender_other_chain,json=senderOtherChain,proto3" json:"sender_other_chain,omitempty"` - RandomNumberHash []byte `protobuf:"bytes,5,opt,name=random_number_hash,json=randomNumberHash,proto3" json:"random_number_hash,omitempty"` //hash of a random number and timestamp, based on SHA256 - Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Amount []*SendOrder_Token `protobuf:"bytes,7,rep,name=amount,proto3" json:"amount,omitempty"` - ExpectedIncome string `protobuf:"bytes,8,opt,name=expected_income,json=expectedIncome,proto3" json:"expected_income,omitempty"` // expected gained token on the other chain - HeightSpan int64 `protobuf:"varint,9,opt,name=height_span,json=heightSpan,proto3" json:"height_span,omitempty"` - CrossChain bool `protobuf:"varint,10,opt,name=cross_chain,json=crossChain,proto3" json:"cross_chain,omitempty"` + // signer address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // recipient address + To []byte `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` + // source on other chain, optional + RecipientOtherChain string `protobuf:"bytes,3,opt,name=recipient_other_chain,json=recipientOtherChain,proto3" json:"recipient_other_chain,omitempty"` + // recipient on other chain, optional + SenderOtherChain string `protobuf:"bytes,4,opt,name=sender_other_chain,json=senderOtherChain,proto3" json:"sender_other_chain,omitempty"` + // hash of a random number and timestamp, based on SHA256 + RandomNumberHash []byte `protobuf:"bytes,5,opt,name=random_number_hash,json=randomNumberHash,proto3" json:"random_number_hash,omitempty"` + // timestamp + Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // amounts + Amount []*SendOrder_Token `protobuf:"bytes,7,rep,name=amount,proto3" json:"amount,omitempty"` + // expected gained token on the other chain + ExpectedIncome string `protobuf:"bytes,8,opt,name=expected_income,json=expectedIncome,proto3" json:"expected_income,omitempty"` + // period expressed in block heights + HeightSpan int64 `protobuf:"varint,9,opt,name=height_span,json=heightSpan,proto3" json:"height_span,omitempty"` + // set for cross-chain send + CrossChain bool `protobuf:"varint,10,opt,name=cross_chain,json=crossChain,proto3" json:"cross_chain,omitempty"` } func (x *HTLTOrder) Reset() { @@ -852,15 +902,18 @@ func (x *HTLTOrder) GetCrossChain() bool { return false } +// Message for Deposit HTLT order type DepositHTLTOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0xB33F9A24 // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // signer address + // signer address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // amounts Amount []*SendOrder_Token `protobuf:"bytes,2,rep,name=amount,proto3" json:"amount,omitempty"` - SwapId []byte `protobuf:"bytes,3,opt,name=swap_id,json=swapId,proto3" json:"swap_id,omitempty"` + // swap ID + SwapId []byte `protobuf:"bytes,3,opt,name=swap_id,json=swapId,proto3" json:"swap_id,omitempty"` } func (x *DepositHTLTOrder) Reset() { @@ -916,14 +969,17 @@ func (x *DepositHTLTOrder) GetSwapId() []byte { return nil } +// Message for Claim HTLT order type ClaimHTLOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0xC1665300 // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // signer address - SwapId []byte `protobuf:"bytes,2,opt,name=swap_id,json=swapId,proto3" json:"swap_id,omitempty"` + // signer address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // swap ID + SwapId []byte `protobuf:"bytes,2,opt,name=swap_id,json=swapId,proto3" json:"swap_id,omitempty"` + // random number input RandomNumber []byte `protobuf:"bytes,3,opt,name=random_number,json=randomNumber,proto3" json:"random_number,omitempty"` } @@ -980,13 +1036,15 @@ func (x *ClaimHTLOrder) GetRandomNumber() []byte { return nil } +// Message for Refund HTLT order type RefundHTLTOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 0x3454A27C // prefix - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // signer address + // signer address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // swap ID SwapId []byte `protobuf:"bytes,2,opt,name=swap_id,json=swapId,proto3" json:"swap_id,omitempty"` } @@ -1036,15 +1094,20 @@ func (x *RefundHTLTOrder) GetSwapId() []byte { return nil } +// Transfer type TransferOut struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` - To []byte `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` - Amount *SendOrder_Token `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` - ExpireTime int64 `protobuf:"varint,4,opt,name=expire_time,json=expireTime,proto3" json:"expire_time,omitempty"` + // source address + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // recipient address + To []byte `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` + // transfer amount + Amount *SendOrder_Token `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` + // expiration time + ExpireTime int64 `protobuf:"varint,4,opt,name=expire_time,json=expireTime,proto3" json:"expire_time,omitempty"` } func (x *TransferOut) Reset() { @@ -1328,16 +1391,20 @@ func (x *SideChainUndelegate) GetChainId() string { return "" } +// Message for TimeLock order type TimeLockOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` // owner address + // owner address + FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` + // Description (optional) Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` // Array of symbol/amount pairs. see SDK https://github.com/binance-chain/javascript-sdk/blob/master/docs/api-docs/classes/tokenmanagement.md#timelock - Amount []*SendOrder_Token `protobuf:"bytes,3,rep,name=amount,proto3" json:"amount,omitempty"` - LockTime int64 `protobuf:"varint,4,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"` + Amount []*SendOrder_Token `protobuf:"bytes,3,rep,name=amount,proto3" json:"amount,omitempty"` + // lock time + LockTime int64 `protobuf:"varint,4,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"` } func (x *TimeLockOrder) Reset() { @@ -1400,17 +1467,22 @@ func (x *TimeLockOrder) GetLockTime() int64 { return 0 } +// Message for TimeRelock order type TimeRelockOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` // owner address - Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` // order ID + // owner address + FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` + // order ID + Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + // Description (optional) Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // Array of symbol/amount pairs. - Amount []*SendOrder_Token `protobuf:"bytes,4,rep,name=amount,proto3" json:"amount,omitempty"` - LockTime int64 `protobuf:"varint,5,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"` + Amount []*SendOrder_Token `protobuf:"bytes,4,rep,name=amount,proto3" json:"amount,omitempty"` + // lock time + LockTime int64 `protobuf:"varint,5,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"` } func (x *TimeRelockOrder) Reset() { @@ -1480,13 +1552,16 @@ func (x *TimeRelockOrder) GetLockTime() int64 { return 0 } +// Message for TimeUnlock order type TimeUnlockOrder struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` // owner address - Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` // order ID + // owner address + FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` + // order ID + Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` } func (x *TimeUnlockOrder) Reset() { @@ -1535,18 +1610,30 @@ func (x *TimeUnlockOrder) GetId() int64 { return 0 } -// Input data necessary to create a signed order. +// Input data necessary to create a signed transaction. type SigningInput struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - AccountNumber int64 `protobuf:"varint,2,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` - Sequence int64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` - Source int64 `protobuf:"varint,4,opt,name=source,proto3" json:"source,omitempty"` - Memo string `protobuf:"bytes,5,opt,name=memo,proto3" json:"memo,omitempty"` - PrivateKey []byte `protobuf:"bytes,6,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + // Chain ID + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // Source account number + AccountNumber int64 `protobuf:"varint,2,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + // Sequence number (account specific) + Sequence int64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` + // Transaction source, see https://github.com/bnb-chain/BEPs/blob/master/BEP10.md + // Some important values: + // 0: Default source value (e.g. for Binance Chain Command Line, or SDKs) + // 1: Binance DEX Web Wallet + // 2: Trust Wallet + Source int64 `protobuf:"varint,4,opt,name=source,proto3" json:"source,omitempty"` + // Optional memo + Memo string `protobuf:"bytes,5,opt,name=memo,proto3" json:"memo,omitempty"` + // The secret private key used for signing (32 bytes). + PrivateKey []byte `protobuf:"bytes,6,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + // Payload message + // // Types that are assignable to OrderOneof: // *SigningInput_TradeOrder // *SigningInput_CancelTradeOrder @@ -1902,7 +1989,7 @@ func (*SigningInput_TimeRelockOrder) isSigningInput_OrderOneof() {} func (*SigningInput_TimeUnlockOrder) isSigningInput_OrderOneof() {} -// Transaction signing output. +// Result containing the signed and encoded transaction. type SigningOutput struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1910,9 +1997,9 @@ type SigningOutput struct { // Signed and encoded transaction bytes. Encoded []byte `protobuf:"bytes,1,opt,name=encoded,proto3" json:"encoded,omitempty"` - /// error code, 0 is ok, other codes will be treated as errors + // OK (=0) or other codes in case of error Error common.SigningError `protobuf:"varint,2,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` - /// error description + // error description in case of error ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` } @@ -1969,59 +2056,22 @@ func (x *SigningOutput) GetErrorMessage() string { return "" } -type Signature_PubKey struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *Signature_PubKey) Reset() { - *x = Signature_PubKey{} - if protoimpl.UnsafeEnabled { - mi := &file_Binance_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Signature_PubKey) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Signature_PubKey) ProtoMessage() {} - -func (x *Signature_PubKey) ProtoReflect() protoreflect.Message { - mi := &file_Binance_proto_msgTypes[23] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Signature_PubKey.ProtoReflect.Descriptor instead. -func (*Signature_PubKey) Descriptor() ([]byte, []int) { - return file_Binance_proto_rawDescGZIP(), []int{1, 0} -} - -// 0x2A2C87FA -// A symbol-amount pair. Could be moved out of SendOrder; kept here for backward compatibility. +// A token amount, symbol-amount pair. Could be moved out of SendOrder; kept here for backward compatibility. type SendOrder_Token struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` - Amount int64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` + // Token ID + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + // Amount + Amount int64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` } func (x *SendOrder_Token) Reset() { *x = SendOrder_Token{} if protoimpl.UnsafeEnabled { - mi := &file_Binance_proto_msgTypes[24] + mi := &file_Binance_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2034,7 +2084,7 @@ func (x *SendOrder_Token) String() string { func (*SendOrder_Token) ProtoMessage() {} func (x *SendOrder_Token) ProtoReflect() protoreflect.Message { - mi := &file_Binance_proto_msgTypes[24] + mi := &file_Binance_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2064,19 +2114,22 @@ func (x *SendOrder_Token) GetAmount() int64 { return 0 } +// Transaction input type SendOrder_Input struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - Coins []*SendOrder_Token `protobuf:"bytes,2,rep,name=coins,proto3" json:"coins,omitempty"` + // source address + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // input coin amounts + Coins []*SendOrder_Token `protobuf:"bytes,2,rep,name=coins,proto3" json:"coins,omitempty"` } func (x *SendOrder_Input) Reset() { *x = SendOrder_Input{} if protoimpl.UnsafeEnabled { - mi := &file_Binance_proto_msgTypes[25] + mi := &file_Binance_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2089,7 +2142,7 @@ func (x *SendOrder_Input) String() string { func (*SendOrder_Input) ProtoMessage() {} func (x *SendOrder_Input) ProtoReflect() protoreflect.Message { - mi := &file_Binance_proto_msgTypes[25] + mi := &file_Binance_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2119,19 +2172,22 @@ func (x *SendOrder_Input) GetCoins() []*SendOrder_Token { return nil } +// Transaction output type SendOrder_Output struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - Coins []*SendOrder_Token `protobuf:"bytes,2,rep,name=coins,proto3" json:"coins,omitempty"` + // destination address + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // output coin amounts + Coins []*SendOrder_Token `protobuf:"bytes,2,rep,name=coins,proto3" json:"coins,omitempty"` } func (x *SendOrder_Output) Reset() { *x = SendOrder_Output{} if protoimpl.UnsafeEnabled { - mi := &file_Binance_proto_msgTypes[26] + mi := &file_Binance_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2144,7 +2200,7 @@ func (x *SendOrder_Output) String() string { func (*SendOrder_Output) ProtoMessage() {} func (x *SendOrder_Output) ProtoReflect() protoreflect.Message { - mi := &file_Binance_proto_msgTypes[26] + mi := &file_Binance_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2188,7 +2244,7 @@ var file_Binance_proto_rawDesc = []byte{ 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x22, 0x8f, 0x01, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x61, 0x74, 0x61, 0x22, 0x85, 0x01, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, @@ -2196,317 +2252,312 @@ var file_Binance_proto_rawDesc = []byte{ 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x1a, 0x08, 0x0a, 0x06, 0x50, - 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0xd2, 0x01, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, + 0x03, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0xd2, 0x01, 0x0a, 0x0a, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70, 0x72, 0x69, + 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x20, + 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x22, 0x58, 0x0a, 0x10, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, - 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, - 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, - 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, - 0x69, 0x6d, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x58, 0x0a, 0x10, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, - 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x14, - 0x0a, 0x05, 0x72, 0x65, 0x66, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, - 0x65, 0x66, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x12, 0x39, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x3c, 0x0a, - 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, - 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x4f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x1a, 0x35, 0x0a, 0x05, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x1a, 0x5a, 0x0a, 0x05, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, + 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x66, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x66, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x09, 0x53, + 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x1a, 0x5b, - 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x0f, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, - 0x72, 0x6f, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, - 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, - 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x75, 0x70, 0x70, - 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x54, - 0x0a, 0x0e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x69, 0x6e, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, - 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x54, 0x0a, 0x0e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x75, 0x72, - 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, + 0x72, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x73, 0x1a, 0x35, 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, + 0x6e, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x65, 0x6e, 0x6f, 0x6d, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x5a, 0x0a, 0x05, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x63, + 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, + 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x63, + 0x6f, 0x69, 0x6e, 0x73, 0x1a, 0x5b, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x63, 0x6f, 0x69, 0x6e, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, + 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x63, 0x6f, 0x69, 0x6e, + 0x73, 0x22, 0x90, 0x01, 0x0a, 0x0f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, + 0x75, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x22, 0x54, 0x0a, 0x0e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x69, 0x6e, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x56, 0x0a, 0x10, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x46, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, + 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x54, 0x0a, 0x0e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x42, 0x75, 0x72, 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x56, 0x0a, 0x10, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, + 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x58, 0x0a, 0x12, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x55, 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x22, 0x58, 0x0a, 0x12, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x6e, 0x66, 0x72, 0x65, - 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, - 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x83, 0x03, 0x0a, - 0x09, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, - 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, - 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x32, - 0x0a, 0x15, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x74, 0x68, 0x65, - 0x72, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, - 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x6f, 0x74, 0x68, - 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, - 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, - 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x72, 0x61, - 0x6e, 0x64, 0x6f, 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, - 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x39, 0x0a, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, - 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x53, 0x70, 0x61, - 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x22, 0x7a, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, - 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, - 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x77, 0x61, 0x70, 0x49, 0x64, 0x22, 0x61, - 0x0a, 0x0d, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x48, 0x54, 0x4c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, - 0x72, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x77, 0x61, 0x70, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, - 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, - 0x72, 0x22, 0x3e, 0x0a, 0x0f, 0x52, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, 0x4c, 0x54, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x70, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x77, 0x61, 0x70, 0x49, - 0x64, 0x22, 0x8d, 0x01, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4f, 0x75, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, - 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, - 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x11, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, - 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x67, - 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, - 0x0a, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x41, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, - 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0a, 0x64, 0x65, - 0x6c, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x49, 0x64, 0x22, 0xee, 0x01, 0x0a, 0x13, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x52, 0x65, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, - 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, - 0x64, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, - 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, - 0x12, 0x2c, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x73, - 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x39, - 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x6e, 0x74, 0x22, 0x83, 0x03, 0x0a, 0x09, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x02, 0x74, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, + 0x74, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x74, + 0x68, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4f, 0x74, 0x68, 0x65, + 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, + 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x10, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x0a, + 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x72, 0x6f, 0x73, 0x73, + 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x72, + 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x7a, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, + 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x73, + 0x77, 0x61, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x77, + 0x61, 0x70, 0x49, 0x64, 0x22, 0x61, 0x0a, 0x0d, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x48, 0x54, 0x4c, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x77, 0x61, + 0x70, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x77, 0x61, 0x70, + 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3e, 0x0a, 0x0f, 0x52, 0x65, 0x66, 0x75, 0x6e, + 0x64, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, + 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x17, + 0x0a, 0x07, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x06, 0x73, 0x77, 0x61, 0x70, 0x49, 0x64, 0x22, 0x8d, 0x01, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x66, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, + 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x39, 0x0a, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, + 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x11, 0x53, 0x69, 0x64, 0x65, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, + 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, + 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x41, 0x0a, 0x0a, 0x64, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, + 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x22, 0xee, 0x01, 0x0a, 0x13, 0x53, 0x69, + 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x67, + 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, + 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x73, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x22, 0xb9, 0x01, 0x0a, 0x13, 0x53, + 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, + 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0d, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, + 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x54, 0x69, 0x6d, 0x65, 0x4c, + 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, + 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, 0x63, + 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, + 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, + 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, + 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x49, 0x64, 0x22, 0xb9, 0x01, 0x0a, 0x13, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, - 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, - 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, - 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, - 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, - 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x22, - 0xbe, 0x01, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, - 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, - 0x22, 0x44, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0xf9, 0x0c, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x69, - 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, - 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x65, 0x71, - 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, - 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x12, 0x3f, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, - 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x12, 0x52, 0x0a, 0x12, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x74, 0x72, - 0x61, 0x64, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x5f, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x54, 0x57, - 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x64, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x47, 0x0a, 0x0c, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x5f, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, - 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, - 0x00, 0x52, 0x0b, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4d, - 0x0a, 0x0e, 0x75, 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, - 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, - 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, - 0x75, 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, 0x0a, - 0x0a, 0x68, 0x74, 0x6c, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, + 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x44, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, + 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, + 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0xf9, 0x0c, 0x0a, + 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x3f, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x64, + 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x74, + 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x52, 0x0a, 0x12, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x5f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, 0x0a, + 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, - 0x52, 0x09, 0x68, 0x74, 0x6c, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x51, 0x0a, 0x11, 0x64, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, 0x54, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, - 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4a, - 0x0a, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x48, 0x54, 0x4c, 0x54, 0x5f, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, - 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, - 0x48, 0x54, 0x4c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x6c, 0x61, 0x69, - 0x6d, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x10, 0x72, 0x65, - 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, 0x4c, 0x54, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x10, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, - 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, - 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x75, 0x6e, - 0x64, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0b, 0x69, 0x73, - 0x73, 0x75, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x41, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, - 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x69, 0x6e, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x0a, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, - 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x42, 0x75, 0x72, 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x62, 0x75, 0x72, - 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, - 0x65, 0x72, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4f, 0x75, - 0x74, 0x48, 0x00, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4f, 0x75, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x55, 0x0a, 0x13, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x64, 0x65, - 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x15, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, - 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x11, 0x73, 0x69, 0x64, 0x65, 0x44, - 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x15, - 0x73, 0x69, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x54, 0x57, - 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, - 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, - 0x74, 0x65, 0x48, 0x00, 0x52, 0x13, 0x73, 0x69, 0x64, 0x65, 0x52, 0x65, 0x64, 0x65, 0x6c, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x15, 0x73, 0x69, 0x64, - 0x65, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, - 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x64, 0x65, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x48, - 0x00, 0x52, 0x13, 0x73, 0x69, 0x64, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x49, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, - 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x48, 0x00, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x12, 0x4f, 0x0a, 0x11, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x09, 0x73, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x47, 0x0a, 0x0c, 0x66, + 0x72, 0x65, 0x65, 0x7a, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x72, 0x65, 0x65, 0x7a, 0x65, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x0e, 0x75, 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x54, + 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x75, 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0a, 0x68, 0x74, 0x6c, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, + 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x54, 0x4c, 0x54, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x68, 0x74, 0x6c, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x12, 0x51, 0x0a, 0x11, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, 0x54, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, - 0x00, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x11, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x48, 0x00, 0x52, 0x10, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, 0x54, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x48, 0x54, 0x4c, + 0x54, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x48, 0x54, 0x4c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x0e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x4e, 0x0a, 0x10, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, 0x4c, 0x54, 0x5f, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, + 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, + 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, + 0x0f, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x44, 0x0a, 0x0b, 0x69, 0x73, 0x73, 0x75, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, + 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x73, + 0x73, 0x75, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x73, 0x73, 0x75, + 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x74, 0x5f, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x54, 0x57, 0x2e, + 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x4d, 0x69, 0x6e, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, + 0x6d, 0x69, 0x6e, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x0a, 0x62, 0x75, 0x72, + 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x48, 0x00, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x6f, 0x6e, 0x65, - 0x6f, 0x66, 0x22, 0x83, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x12, 0x33, - 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, - 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x56, 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, - 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x75, 0x72, 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, + 0x00, 0x52, 0x09, 0x62, 0x75, 0x72, 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x12, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x66, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x48, 0x00, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x66, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x55, 0x0a, 0x13, 0x73, + 0x69, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x64, 0x65, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, + 0x11, 0x73, 0x69, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x15, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x6c, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, + 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x13, 0x73, 0x69, 0x64, 0x65, + 0x52, 0x65, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, + 0x5b, 0x0a, 0x15, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, + 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x55, 0x6e, 0x64, 0x65, 0x6c, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x13, 0x73, 0x69, 0x64, 0x65, 0x55, 0x6e, 0x64, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x49, 0x0a, 0x0f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, + 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, + 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, + 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x11, 0x74, 0x69, 0x6d, 0x65, 0x5f, + 0x72, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x6c, 0x6f, 0x63, 0x6b, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x6c, + 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x11, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x1a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, + 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x6e, + 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x5f, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x83, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x65, 0x64, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x17, + 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, + 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2521,7 +2572,7 @@ func file_Binance_proto_rawDescGZIP() []byte { return file_Binance_proto_rawDescData } -var file_Binance_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_Binance_proto_msgTypes = make([]protoimpl.MessageInfo, 26) var file_Binance_proto_goTypes = []interface{}{ (*Transaction)(nil), // 0: TW.Binance.Proto.Transaction (*Signature)(nil), // 1: TW.Binance.Proto.Signature @@ -2546,23 +2597,22 @@ var file_Binance_proto_goTypes = []interface{}{ (*TimeUnlockOrder)(nil), // 20: TW.Binance.Proto.TimeUnlockOrder (*SigningInput)(nil), // 21: TW.Binance.Proto.SigningInput (*SigningOutput)(nil), // 22: TW.Binance.Proto.SigningOutput - (*Signature_PubKey)(nil), // 23: TW.Binance.Proto.Signature.PubKey - (*SendOrder_Token)(nil), // 24: TW.Binance.Proto.SendOrder.Token - (*SendOrder_Input)(nil), // 25: TW.Binance.Proto.SendOrder.Input - (*SendOrder_Output)(nil), // 26: TW.Binance.Proto.SendOrder.Output - (common.SigningError)(0), // 27: TW.Common.Proto.SigningError + (*SendOrder_Token)(nil), // 23: TW.Binance.Proto.SendOrder.Token + (*SendOrder_Input)(nil), // 24: TW.Binance.Proto.SendOrder.Input + (*SendOrder_Output)(nil), // 25: TW.Binance.Proto.SendOrder.Output + (common.SigningError)(0), // 26: TW.Common.Proto.SigningError } var file_Binance_proto_depIdxs = []int32{ - 25, // 0: TW.Binance.Proto.SendOrder.inputs:type_name -> TW.Binance.Proto.SendOrder.Input - 26, // 1: TW.Binance.Proto.SendOrder.outputs:type_name -> TW.Binance.Proto.SendOrder.Output - 24, // 2: TW.Binance.Proto.HTLTOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token - 24, // 3: TW.Binance.Proto.DepositHTLTOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token - 24, // 4: TW.Binance.Proto.TransferOut.amount:type_name -> TW.Binance.Proto.SendOrder.Token - 24, // 5: TW.Binance.Proto.SideChainDelegate.delegation:type_name -> TW.Binance.Proto.SendOrder.Token - 24, // 6: TW.Binance.Proto.SideChainRedelegate.amount:type_name -> TW.Binance.Proto.SendOrder.Token - 24, // 7: TW.Binance.Proto.SideChainUndelegate.amount:type_name -> TW.Binance.Proto.SendOrder.Token - 24, // 8: TW.Binance.Proto.TimeLockOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token - 24, // 9: TW.Binance.Proto.TimeRelockOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 0: TW.Binance.Proto.SendOrder.inputs:type_name -> TW.Binance.Proto.SendOrder.Input + 25, // 1: TW.Binance.Proto.SendOrder.outputs:type_name -> TW.Binance.Proto.SendOrder.Output + 23, // 2: TW.Binance.Proto.HTLTOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 23, // 3: TW.Binance.Proto.DepositHTLTOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 23, // 4: TW.Binance.Proto.TransferOut.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 23, // 5: TW.Binance.Proto.SideChainDelegate.delegation:type_name -> TW.Binance.Proto.SendOrder.Token + 23, // 6: TW.Binance.Proto.SideChainRedelegate.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 23, // 7: TW.Binance.Proto.SideChainUndelegate.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 23, // 8: TW.Binance.Proto.TimeLockOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 23, // 9: TW.Binance.Proto.TimeRelockOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token 2, // 10: TW.Binance.Proto.SigningInput.trade_order:type_name -> TW.Binance.Proto.TradeOrder 3, // 11: TW.Binance.Proto.SigningInput.cancel_trade_order:type_name -> TW.Binance.Proto.CancelTradeOrder 4, // 12: TW.Binance.Proto.SigningInput.send_order:type_name -> TW.Binance.Proto.SendOrder @@ -2582,9 +2632,9 @@ var file_Binance_proto_depIdxs = []int32{ 18, // 26: TW.Binance.Proto.SigningInput.time_lock_order:type_name -> TW.Binance.Proto.TimeLockOrder 19, // 27: TW.Binance.Proto.SigningInput.time_relock_order:type_name -> TW.Binance.Proto.TimeRelockOrder 20, // 28: TW.Binance.Proto.SigningInput.time_unlock_order:type_name -> TW.Binance.Proto.TimeUnlockOrder - 27, // 29: TW.Binance.Proto.SigningOutput.error:type_name -> TW.Common.Proto.SigningError - 24, // 30: TW.Binance.Proto.SendOrder.Input.coins:type_name -> TW.Binance.Proto.SendOrder.Token - 24, // 31: TW.Binance.Proto.SendOrder.Output.coins:type_name -> TW.Binance.Proto.SendOrder.Token + 26, // 29: TW.Binance.Proto.SigningOutput.error:type_name -> TW.Common.Proto.SigningError + 23, // 30: TW.Binance.Proto.SendOrder.Input.coins:type_name -> TW.Binance.Proto.SendOrder.Token + 23, // 31: TW.Binance.Proto.SendOrder.Output.coins:type_name -> TW.Binance.Proto.SendOrder.Token 32, // [32:32] is the sub-list for method output_type 32, // [32:32] is the sub-list for method input_type 32, // [32:32] is the sub-list for extension type_name @@ -2875,18 +2925,6 @@ func file_Binance_proto_init() { } } file_Binance_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Signature_PubKey); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_Binance_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendOrder_Token); i { case 0: return &v.state @@ -2898,7 +2936,7 @@ func file_Binance_proto_init() { return nil } } - file_Binance_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_Binance_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendOrder_Input); i { case 0: return &v.state @@ -2910,7 +2948,7 @@ func file_Binance_proto_init() { return nil } } - file_Binance_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_Binance_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendOrder_Output); i { case 0: return &v.state @@ -2950,7 +2988,7 @@ func file_Binance_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_Binance_proto_rawDesc, NumEnums: 0, - NumMessages: 27, + NumMessages: 26, NumExtensions: 0, NumServices: 0, }, diff --git a/samples/go/protos/bitcoin/Bitcoin.pb.go b/samples/go/protos/bitcoin/Bitcoin.pb.go index 88b884e6912..1758d1bd22e 100644 --- a/samples/go/protos/bitcoin/Bitcoin.pb.go +++ b/samples/go/protos/bitcoin/Bitcoin.pb.go @@ -1,17 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.19.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.5 // source: Bitcoin.proto package bitcoin import ( - common "tw/protos/common" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + common "tw/protos/common" ) const ( @@ -21,6 +21,7 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// A transaction, with its inputs and outputs type Transaction struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -32,7 +33,7 @@ type Transaction struct { LockTime uint32 `protobuf:"varint,2,opt,name=lockTime,proto3" json:"lockTime,omitempty"` // A list of 1 or more transaction inputs or sources for coins. Inputs []*TransactionInput `protobuf:"bytes,3,rep,name=inputs,proto3" json:"inputs,omitempty"` - // A list of 1 or more transaction outputs or destinations for coins + // A list of 1 or more transaction outputs or destinations for coins. Outputs []*TransactionOutput `protobuf:"bytes,4,rep,name=outputs,proto3" json:"outputs,omitempty"` } @@ -169,7 +170,7 @@ type OutPoint struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The hash of the referenced transaction. + // The hash of the referenced transaction (network byte order, usually needs to be reversed). Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` // The index of the specific output in the transaction. Index uint32 `protobuf:"varint,2,opt,name=index,proto3" json:"index,omitempty"` @@ -355,64 +356,6 @@ func (x *UnspentTransaction) GetAmount() int64 { return 0 } -// Pair of destination address and amount, used for extra outputs -type OutputAddress struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Destination address - ToAddress string `protobuf:"bytes,1,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` - // Amount to be paid to this output - Amount int64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` -} - -func (x *OutputAddress) Reset() { - *x = OutputAddress{} - if protoimpl.UnsafeEnabled { - mi := &file_Bitcoin_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OutputAddress) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OutputAddress) ProtoMessage() {} - -func (x *OutputAddress) ProtoReflect() protoreflect.Message { - mi := &file_Bitcoin_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OutputAddress.ProtoReflect.Descriptor instead. -func (*OutputAddress) Descriptor() ([]byte, []int) { - return file_Bitcoin_proto_rawDescGZIP(), []int{5} -} - -func (x *OutputAddress) GetToAddress() string { - if x != nil { - return x.ToAddress - } - return "" -} - -func (x *OutputAddress) GetAmount() int64 { - if x != nil { - return x.Amount - } - return 0 -} - // Input data necessary to create a signed transaction. type SigningInput struct { state protoimpl.MessageState @@ -425,22 +368,23 @@ type SigningInput struct { // except when use_max_amount is set, in that case this amount is not relevant, maximum possible amount will be used (max avail less fee). // If amount is equal or more than the available amount, also max amount will be used. Amount int64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` - // Transaction fee per byte. + // Transaction fee rate, satoshis per byte, used to compute required fee (when planning) ByteFee int64 `protobuf:"varint,3,opt,name=byte_fee,json=byteFee,proto3" json:"byte_fee,omitempty"` - // Recipient's address. + // Recipient's address, as string. ToAddress string `protobuf:"bytes,4,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` - // Change address. + // Change address, as string. ChangeAddress string `protobuf:"bytes,5,opt,name=change_address,json=changeAddress,proto3" json:"change_address,omitempty"` - // Available private keys. + // The available secret private key or keys required for signing (32 bytes each). PrivateKey [][]byte `protobuf:"bytes,6,rep,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` // Available redeem scripts indexed by script hash. Scripts map[string][]byte `protobuf:"bytes,7,rep,name=scripts,proto3" json:"scripts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // Available unspent transaction outputs. + // Available input unspent transaction outputs. Utxo []*UnspentTransaction `protobuf:"bytes,8,rep,name=utxo,proto3" json:"utxo,omitempty"` - // If sending max amount. - UseMaxAmount bool `protobuf:"varint,9,opt,name=use_max_amount,json=useMaxAmount,proto3" json:"use_max_amount,omitempty"` - CoinType uint32 `protobuf:"varint,10,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` - // Optional transaction plan + // Set if sending max amount is requested. + UseMaxAmount bool `protobuf:"varint,9,opt,name=use_max_amount,json=useMaxAmount,proto3" json:"use_max_amount,omitempty"` + // Coin type (used by forks). + CoinType uint32 `protobuf:"varint,10,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` + // Optional transaction plan. If missing, plan will be computed. Plan *TransactionPlan `protobuf:"bytes,11,opt,name=plan,proto3" json:"plan,omitempty"` // Optional lockTime, default value 0 means no time locking. // If all inputs have final (`0xffffffff`) sequence numbers then `lockTime` is irrelevant. @@ -450,18 +394,12 @@ type SigningInput struct { LockTime uint32 `protobuf:"varint,12,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"` // Optional zero-amount, OP_RETURN output OutputOpReturn []byte `protobuf:"bytes,13,opt,name=output_op_return,json=outputOpReturn,proto3" json:"output_op_return,omitempty"` - // Optional additional destination addresses, additional to first to_address output - ExtraOutputs []*OutputAddress `protobuf:"bytes,14,rep,name=extra_outputs,json=extraOutputs,proto3" json:"extra_outputs,omitempty"` - // If use max utxo. - UseMaxUtxo bool `protobuf:"varint,15,opt,name=use_max_utxo,json=useMaxUtxo,proto3" json:"use_max_utxo,omitempty"` - // If disable dust filter. - DisableDustFilter bool `protobuf:"varint,16,opt,name=disable_dust_filter,json=disableDustFilter,proto3" json:"disable_dust_filter,omitempty"` } func (x *SigningInput) Reset() { *x = SigningInput{} if protoimpl.UnsafeEnabled { - mi := &file_Bitcoin_proto_msgTypes[6] + mi := &file_Bitcoin_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -474,7 +412,7 @@ func (x *SigningInput) String() string { func (*SigningInput) ProtoMessage() {} func (x *SigningInput) ProtoReflect() protoreflect.Message { - mi := &file_Bitcoin_proto_msgTypes[6] + mi := &file_Bitcoin_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -487,7 +425,7 @@ func (x *SigningInput) ProtoReflect() protoreflect.Message { // Deprecated: Use SigningInput.ProtoReflect.Descriptor instead. func (*SigningInput) Descriptor() ([]byte, []int) { - return file_Bitcoin_proto_rawDescGZIP(), []int{6} + return file_Bitcoin_proto_rawDescGZIP(), []int{5} } func (x *SigningInput) GetHashType() uint32 { @@ -581,27 +519,6 @@ func (x *SigningInput) GetOutputOpReturn() []byte { return nil } -func (x *SigningInput) GetExtraOutputs() []*OutputAddress { - if x != nil { - return x.ExtraOutputs - } - return nil -} - -func (x *SigningInput) GetUseMaxUtxo() bool { - if x != nil { - return x.UseMaxUtxo - } - return false -} - -func (x *SigningInput) GetDisableDustFilter() bool { - if x != nil { - return x.DisableDustFilter - } - return false -} - // Describes a preliminary transaction plan. type TransactionPlan struct { state protoimpl.MessageState @@ -610,13 +527,13 @@ type TransactionPlan struct { // Amount to be received at the other end. Amount int64 `protobuf:"varint,1,opt,name=amount,proto3" json:"amount,omitempty"` - // Maximum available amount. + // Maximum available amount in all the input UTXOs. AvailableAmount int64 `protobuf:"varint,2,opt,name=available_amount,json=availableAmount,proto3" json:"available_amount,omitempty"` // Estimated transaction fee. Fee int64 `protobuf:"varint,3,opt,name=fee,proto3" json:"fee,omitempty"` - // Change. + // Remaining change Change int64 `protobuf:"varint,4,opt,name=change,proto3" json:"change,omitempty"` - // Selected unspent transaction outputs. + // Selected unspent transaction outputs (subset of all input UTXOs) Utxos []*UnspentTransaction `protobuf:"bytes,5,rep,name=utxos,proto3" json:"utxos,omitempty"` // Zcash branch id BranchId []byte `protobuf:"bytes,6,opt,name=branch_id,json=branchId,proto3" json:"branch_id,omitempty"` @@ -629,7 +546,7 @@ type TransactionPlan struct { func (x *TransactionPlan) Reset() { *x = TransactionPlan{} if protoimpl.UnsafeEnabled { - mi := &file_Bitcoin_proto_msgTypes[7] + mi := &file_Bitcoin_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -642,7 +559,7 @@ func (x *TransactionPlan) String() string { func (*TransactionPlan) ProtoMessage() {} func (x *TransactionPlan) ProtoReflect() protoreflect.Message { - mi := &file_Bitcoin_proto_msgTypes[7] + mi := &file_Bitcoin_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -655,7 +572,7 @@ func (x *TransactionPlan) ProtoReflect() protoreflect.Message { // Deprecated: Use TransactionPlan.ProtoReflect.Descriptor instead. func (*TransactionPlan) Descriptor() ([]byte, []int) { - return file_Bitcoin_proto_rawDescGZIP(), []int{7} + return file_Bitcoin_proto_rawDescGZIP(), []int{6} } func (x *TransactionPlan) GetAmount() int64 { @@ -714,17 +631,18 @@ func (x *TransactionPlan) GetOutputOpReturn() []byte { return nil } -// Transaction signing output. +// Result containing the signed and encoded transaction. +// Note that the amount may be different than the requested amount to account for fees and available funds. type SigningOutput struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Resulting transaction. Note that the amount may be different than the requested amount to account for fees and available funds. + // Resulting transaction. Transaction *Transaction `protobuf:"bytes,1,opt,name=transaction,proto3" json:"transaction,omitempty"` // Signed and encoded transaction bytes. Encoded []byte `protobuf:"bytes,2,opt,name=encoded,proto3" json:"encoded,omitempty"` - // Transaction id + // Transaction ID (hash) TransactionId string `protobuf:"bytes,3,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` // Optional error Error common.SigningError `protobuf:"varint,4,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` @@ -735,7 +653,7 @@ type SigningOutput struct { func (x *SigningOutput) Reset() { *x = SigningOutput{} if protoimpl.UnsafeEnabled { - mi := &file_Bitcoin_proto_msgTypes[8] + mi := &file_Bitcoin_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -748,7 +666,7 @@ func (x *SigningOutput) String() string { func (*SigningOutput) ProtoMessage() {} func (x *SigningOutput) ProtoReflect() protoreflect.Message { - mi := &file_Bitcoin_proto_msgTypes[8] + mi := &file_Bitcoin_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -761,7 +679,7 @@ func (x *SigningOutput) ProtoReflect() protoreflect.Message { // Deprecated: Use SigningOutput.ProtoReflect.Descriptor instead. func (*SigningOutput) Descriptor() ([]byte, []int) { - return file_Bitcoin_proto_rawDescGZIP(), []int{8} + return file_Bitcoin_proto_rawDescGZIP(), []int{7} } func (x *SigningOutput) GetTransaction() *Transaction { @@ -799,6 +717,7 @@ func (x *SigningOutput) GetErrorMessage() string { return "" } +/// Pre-image hash to be used for signing type HashPublicKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -813,7 +732,7 @@ type HashPublicKey struct { func (x *HashPublicKey) Reset() { *x = HashPublicKey{} if protoimpl.UnsafeEnabled { - mi := &file_Bitcoin_proto_msgTypes[9] + mi := &file_Bitcoin_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -826,7 +745,7 @@ func (x *HashPublicKey) String() string { func (*HashPublicKey) ProtoMessage() {} func (x *HashPublicKey) ProtoReflect() protoreflect.Message { - mi := &file_Bitcoin_proto_msgTypes[9] + mi := &file_Bitcoin_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -839,7 +758,7 @@ func (x *HashPublicKey) ProtoReflect() protoreflect.Message { // Deprecated: Use HashPublicKey.ProtoReflect.Descriptor instead. func (*HashPublicKey) Descriptor() ([]byte, []int) { - return file_Bitcoin_proto_rawDescGZIP(), []int{9} + return file_Bitcoin_proto_rawDescGZIP(), []int{8} } func (x *HashPublicKey) GetDataHash() []byte { @@ -873,7 +792,7 @@ type PreSigningOutput struct { func (x *PreSigningOutput) Reset() { *x = PreSigningOutput{} if protoimpl.UnsafeEnabled { - mi := &file_Bitcoin_proto_msgTypes[10] + mi := &file_Bitcoin_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -886,7 +805,7 @@ func (x *PreSigningOutput) String() string { func (*PreSigningOutput) ProtoMessage() {} func (x *PreSigningOutput) ProtoReflect() protoreflect.Message { - mi := &file_Bitcoin_proto_msgTypes[10] + mi := &file_Bitcoin_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -899,7 +818,7 @@ func (x *PreSigningOutput) ProtoReflect() protoreflect.Message { // Deprecated: Use PreSigningOutput.ProtoReflect.Descriptor instead. func (*PreSigningOutput) Descriptor() ([]byte, []int) { - return file_Bitcoin_proto_rawDescGZIP(), []int{10} + return file_Bitcoin_proto_rawDescGZIP(), []int{9} } func (x *PreSigningOutput) GetHashPublicKeys() []*HashPublicKey { @@ -967,53 +886,39 @@ var file_Bitcoin_proto_rawDesc = []byte{ 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x22, 0x46, 0x0a, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xdb, 0x05, 0x0a, 0x0c, 0x53, 0x69, - 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x61, - 0x73, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x68, - 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, - 0x79, 0x12, 0x45, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x04, 0x75, 0x74, 0x78, 0x6f, - 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, - 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, - 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x75, 0x74, - 0x78, 0x6f, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x4d, - 0x61, 0x78, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x6f, 0x69, - 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, - 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x1b, 0x0a, 0x09, - 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x5f, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4f, 0x70, 0x52, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x12, 0x44, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x54, 0x57, 0x2e, - 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, - 0x72, 0x61, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x73, 0x65, - 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x75, 0x73, 0x65, 0x4d, 0x61, 0x78, 0x55, 0x74, 0x78, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x75, 0x73, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x44, 0x75, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, + 0x74, 0x22, 0xc3, 0x04, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x68, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x5f, + 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x46, + 0x65, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x45, 0x0a, 0x07, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x54, 0x57, 0x2e, + 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, + 0x12, 0x38, 0x0a, 0x04, 0x75, 0x74, 0x78, 0x6f, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, + 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x75, 0x74, 0x78, 0x6f, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x73, + 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x4d, 0x61, 0x78, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x35, 0x0a, + 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, + 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x04, + 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6f, 0x70, 0x5f, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x4f, 0x70, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, @@ -1068,13 +973,9 @@ var file_Bitcoin_proto_rawDesc = []byte{ 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x58, + 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x17, 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, - 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x75, - 0x74, 0x78, 0x6f, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1089,42 +990,40 @@ func file_Bitcoin_proto_rawDescGZIP() []byte { return file_Bitcoin_proto_rawDescData } -var file_Bitcoin_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_Bitcoin_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_Bitcoin_proto_goTypes = []interface{}{ (*Transaction)(nil), // 0: TW.Bitcoin.Proto.Transaction (*TransactionInput)(nil), // 1: TW.Bitcoin.Proto.TransactionInput (*OutPoint)(nil), // 2: TW.Bitcoin.Proto.OutPoint (*TransactionOutput)(nil), // 3: TW.Bitcoin.Proto.TransactionOutput (*UnspentTransaction)(nil), // 4: TW.Bitcoin.Proto.UnspentTransaction - (*OutputAddress)(nil), // 5: TW.Bitcoin.Proto.OutputAddress - (*SigningInput)(nil), // 6: TW.Bitcoin.Proto.SigningInput - (*TransactionPlan)(nil), // 7: TW.Bitcoin.Proto.TransactionPlan - (*SigningOutput)(nil), // 8: TW.Bitcoin.Proto.SigningOutput - (*HashPublicKey)(nil), // 9: TW.Bitcoin.Proto.HashPublicKey - (*PreSigningOutput)(nil), // 10: TW.Bitcoin.Proto.PreSigningOutput - nil, // 11: TW.Bitcoin.Proto.SigningInput.ScriptsEntry - (common.SigningError)(0), // 12: TW.Common.Proto.SigningError + (*SigningInput)(nil), // 5: TW.Bitcoin.Proto.SigningInput + (*TransactionPlan)(nil), // 6: TW.Bitcoin.Proto.TransactionPlan + (*SigningOutput)(nil), // 7: TW.Bitcoin.Proto.SigningOutput + (*HashPublicKey)(nil), // 8: TW.Bitcoin.Proto.HashPublicKey + (*PreSigningOutput)(nil), // 9: TW.Bitcoin.Proto.PreSigningOutput + nil, // 10: TW.Bitcoin.Proto.SigningInput.ScriptsEntry + (common.SigningError)(0), // 11: TW.Common.Proto.SigningError } var file_Bitcoin_proto_depIdxs = []int32{ 1, // 0: TW.Bitcoin.Proto.Transaction.inputs:type_name -> TW.Bitcoin.Proto.TransactionInput 3, // 1: TW.Bitcoin.Proto.Transaction.outputs:type_name -> TW.Bitcoin.Proto.TransactionOutput 2, // 2: TW.Bitcoin.Proto.TransactionInput.previousOutput:type_name -> TW.Bitcoin.Proto.OutPoint 2, // 3: TW.Bitcoin.Proto.UnspentTransaction.out_point:type_name -> TW.Bitcoin.Proto.OutPoint - 11, // 4: TW.Bitcoin.Proto.SigningInput.scripts:type_name -> TW.Bitcoin.Proto.SigningInput.ScriptsEntry + 10, // 4: TW.Bitcoin.Proto.SigningInput.scripts:type_name -> TW.Bitcoin.Proto.SigningInput.ScriptsEntry 4, // 5: TW.Bitcoin.Proto.SigningInput.utxo:type_name -> TW.Bitcoin.Proto.UnspentTransaction - 7, // 6: TW.Bitcoin.Proto.SigningInput.plan:type_name -> TW.Bitcoin.Proto.TransactionPlan - 5, // 7: TW.Bitcoin.Proto.SigningInput.extra_outputs:type_name -> TW.Bitcoin.Proto.OutputAddress - 4, // 8: TW.Bitcoin.Proto.TransactionPlan.utxos:type_name -> TW.Bitcoin.Proto.UnspentTransaction - 12, // 9: TW.Bitcoin.Proto.TransactionPlan.error:type_name -> TW.Common.Proto.SigningError - 0, // 10: TW.Bitcoin.Proto.SigningOutput.transaction:type_name -> TW.Bitcoin.Proto.Transaction - 12, // 11: TW.Bitcoin.Proto.SigningOutput.error:type_name -> TW.Common.Proto.SigningError - 9, // 12: TW.Bitcoin.Proto.PreSigningOutput.hash_public_keys:type_name -> TW.Bitcoin.Proto.HashPublicKey - 12, // 13: TW.Bitcoin.Proto.PreSigningOutput.error:type_name -> TW.Common.Proto.SigningError - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 6, // 6: TW.Bitcoin.Proto.SigningInput.plan:type_name -> TW.Bitcoin.Proto.TransactionPlan + 4, // 7: TW.Bitcoin.Proto.TransactionPlan.utxos:type_name -> TW.Bitcoin.Proto.UnspentTransaction + 11, // 8: TW.Bitcoin.Proto.TransactionPlan.error:type_name -> TW.Common.Proto.SigningError + 0, // 9: TW.Bitcoin.Proto.SigningOutput.transaction:type_name -> TW.Bitcoin.Proto.Transaction + 11, // 10: TW.Bitcoin.Proto.SigningOutput.error:type_name -> TW.Common.Proto.SigningError + 8, // 11: TW.Bitcoin.Proto.PreSigningOutput.hash_public_keys:type_name -> TW.Bitcoin.Proto.HashPublicKey + 11, // 12: TW.Bitcoin.Proto.PreSigningOutput.error:type_name -> TW.Common.Proto.SigningError + 13, // [13:13] is the sub-list for method output_type + 13, // [13:13] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_Bitcoin_proto_init() } @@ -1194,18 +1093,6 @@ func file_Bitcoin_proto_init() { } } file_Bitcoin_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OutputAddress); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_Bitcoin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SigningInput); i { case 0: return &v.state @@ -1217,7 +1104,7 @@ func file_Bitcoin_proto_init() { return nil } } - file_Bitcoin_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_Bitcoin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TransactionPlan); i { case 0: return &v.state @@ -1229,7 +1116,7 @@ func file_Bitcoin_proto_init() { return nil } } - file_Bitcoin_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_Bitcoin_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SigningOutput); i { case 0: return &v.state @@ -1241,7 +1128,7 @@ func file_Bitcoin_proto_init() { return nil } } - file_Bitcoin_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_Bitcoin_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HashPublicKey); i { case 0: return &v.state @@ -1253,7 +1140,7 @@ func file_Bitcoin_proto_init() { return nil } } - file_Bitcoin_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_Bitcoin_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PreSigningOutput); i { case 0: return &v.state @@ -1272,7 +1159,7 @@ func file_Bitcoin_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_Bitcoin_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 11, NumExtensions: 0, NumServices: 0, }, diff --git a/samples/go/protos/common/Common.pb.go b/samples/go/protos/common/Common.pb.go index bc50f992214..ddf4fd142cc 100644 --- a/samples/go/protos/common/Common.pb.go +++ b/samples/go/protos/common/Common.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.19.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.5 // source: Common.proto package common @@ -20,38 +20,65 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Error codes, used in multiple blockchains. type SigningError int32 const ( - SigningError_OK SigningError = 0 // OK - // chain-generic, generic - SigningError_Error_general SigningError = 1 + // This is the OK case, with value=0 + SigningError_OK SigningError = 0 + // Chain-generic codes: + // Generic error (used if there is no suitable specific error is adequate) + SigningError_Error_general SigningError = 1 + // Internal error, indicates some very unusual, unexpected case SigningError_Error_internal SigningError = 2 - // chain-generic, input - SigningError_Error_low_balance SigningError = 3 - SigningError_Error_zero_amount_requested SigningError = 4 // Requested amount is zero - SigningError_Error_missing_private_key SigningError = 5 - SigningError_Error_invalid_private_key SigningError = 15 - SigningError_Error_invalid_address SigningError = 16 - SigningError_Error_invalid_utxo SigningError = 17 - SigningError_Error_invalid_utxo_amount SigningError = 18 - // chain-generic, fee + // Chain-generic codes, input related: + // Low balance: the sender balance is not enough to cover the send and other auxiliary amount such as fee, deposit, or minimal balance. + SigningError_Error_low_balance SigningError = 3 + // Requested amount is zero, send of 0 makes no sense + SigningError_Error_zero_amount_requested SigningError = 4 + // One required key is missing (too few or wrong keys are provided) + SigningError_Error_missing_private_key SigningError = 5 + // A private key provided is invalid (e.g. wrong size, usually should be 32 bytes) + SigningError_Error_invalid_private_key SigningError = 15 + // A provided address (e.g. destination address) is invalid + SigningError_Error_invalid_address SigningError = 16 + // A provided input UTXO is invalid + SigningError_Error_invalid_utxo SigningError = 17 + // The amount of an input UTXO is invalid + SigningError_Error_invalid_utxo_amount SigningError = 18 + // Chain-generic, fee related: + // Wrong fee is given, probably it is too low to cover minimal fee for the transaction SigningError_Error_wrong_fee SigningError = 6 - // chain-generic, signing - SigningError_Error_signing SigningError = 7 - SigningError_Error_tx_too_big SigningError = 8 // [NEO] Transaction too big, fee in GAS needed or try send by parts - // UTXO-chain specific, inputs - SigningError_Error_missing_input_utxos SigningError = 9 // No UTXOs provided [BTC] - SigningError_Error_not_enough_utxos SigningError = 10 // Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out) [BTC] - // UTXO-chain specific, script - SigningError_Error_script_redeem SigningError = 11 // [BTC] Missing redeem script - SigningError_Error_script_output SigningError = 12 // [BTC] Invalid output script - SigningError_Error_script_witness_program SigningError = 13 // [BTC] Unrecognized witness program - SigningError_Error_invalid_memo SigningError = 14 // e.g. [XRP] Invalid tag - SigningError_Error_input_parse SigningError = 19 // e.g. Invalid input data - SigningError_Error_no_support_n2n SigningError = 20 // e.g. Not support n2n transaction - SigningError_Error_signatures_count SigningError = 21 // Incorrect count of signatures passed to compile - SigningError_Error_invalid_params SigningError = 22 // Incorrect parameters + // Chain-generic, signing related: + // General signing error + SigningError_Error_signing SigningError = 7 + // Resulting transaction is too large + // [NEO] Transaction too big, fee in GAS needed or try send by parts + SigningError_Error_tx_too_big SigningError = 8 + // UTXO-chain specific, input related: + // No input UTXOs provided [BTC] + SigningError_Error_missing_input_utxos SigningError = 9 + // Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out) [BTC] + SigningError_Error_not_enough_utxos SigningError = 10 + // UTXO-chain specific, script related: + // [BTC] Missing required redeem script + SigningError_Error_script_redeem SigningError = 11 + // [BTC] Invalid required output script + SigningError_Error_script_output SigningError = 12 + // [BTC] Unrecognized witness program + SigningError_Error_script_witness_program SigningError = 13 + // Invalid memo, e.g. [XRP] Invalid tag + SigningError_Error_invalid_memo SigningError = 14 + // Some input field cannot be parsed + SigningError_Error_input_parse SigningError = 19 + // Multi-input and multi-output transaction not supported + SigningError_Error_no_support_n2n SigningError = 20 + // Incorrect count of signatures passed to compile + SigningError_Error_signatures_count SigningError = 21 + // Incorrect input parameter + SigningError_Error_invalid_params SigningError = 22 + // Invalid input token amount + SigningError_Error_invalid_requested_token_amount SigningError = 23 ) // Enum value maps for SigningError. @@ -80,31 +107,33 @@ var ( 20: "Error_no_support_n2n", 21: "Error_signatures_count", 22: "Error_invalid_params", + 23: "Error_invalid_requested_token_amount", } SigningError_value = map[string]int32{ - "OK": 0, - "Error_general": 1, - "Error_internal": 2, - "Error_low_balance": 3, - "Error_zero_amount_requested": 4, - "Error_missing_private_key": 5, - "Error_invalid_private_key": 15, - "Error_invalid_address": 16, - "Error_invalid_utxo": 17, - "Error_invalid_utxo_amount": 18, - "Error_wrong_fee": 6, - "Error_signing": 7, - "Error_tx_too_big": 8, - "Error_missing_input_utxos": 9, - "Error_not_enough_utxos": 10, - "Error_script_redeem": 11, - "Error_script_output": 12, - "Error_script_witness_program": 13, - "Error_invalid_memo": 14, - "Error_input_parse": 19, - "Error_no_support_n2n": 20, - "Error_signatures_count": 21, - "Error_invalid_params": 22, + "OK": 0, + "Error_general": 1, + "Error_internal": 2, + "Error_low_balance": 3, + "Error_zero_amount_requested": 4, + "Error_missing_private_key": 5, + "Error_invalid_private_key": 15, + "Error_invalid_address": 16, + "Error_invalid_utxo": 17, + "Error_invalid_utxo_amount": 18, + "Error_wrong_fee": 6, + "Error_signing": 7, + "Error_tx_too_big": 8, + "Error_missing_input_utxos": 9, + "Error_not_enough_utxos": 10, + "Error_script_redeem": 11, + "Error_script_output": 12, + "Error_script_witness_program": 13, + "Error_invalid_memo": 14, + "Error_input_parse": 19, + "Error_no_support_n2n": 20, + "Error_signatures_count": 21, + "Error_invalid_params": 22, + "Error_invalid_requested_token_amount": 23, } ) @@ -140,7 +169,7 @@ var File_Common_proto protoreflect.FileDescriptor var file_Common_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2a, - 0xd1, 0x04, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0xfb, 0x04, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x10, 0x02, 0x12, @@ -177,13 +206,11 @@ var file_Common_proto_rawDesc = []byte{ 0x16, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x15, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x10, 0x16, 0x42, 0x55, 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x6a, 0x6e, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x3c, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, - 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x69, 0x6e, 0x74, - 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x73, 0x10, 0x16, 0x12, 0x28, 0x0a, 0x24, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x17, 0x42, 0x17, 0x0a, + 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, 0x69, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/samples/go/protos/ethereum/Ethereum.pb.go b/samples/go/protos/ethereum/Ethereum.pb.go index 14707736bce..ef2cad5cac3 100644 --- a/samples/go/protos/ethereum/Ethereum.pb.go +++ b/samples/go/protos/ethereum/Ethereum.pb.go @@ -1,17 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.19.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.5 // source: Ethereum.proto package ethereum import ( - common "tw/protos/common" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + common "tw/protos/common" ) const ( @@ -21,11 +21,14 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Transaction type type TransactionMode int32 const ( - TransactionMode_Legacy TransactionMode = 0 // Legacy transaction, pre-EIP2718/EIP1559; for fee gasPrice/gasLimit is used - TransactionMode_Enveloped TransactionMode = 1 // Enveloped transaction EIP2718 (with type 0x2), fee is according to EIP1559 (base fee, inclusion fee, ...) + // Legacy transaction, pre-EIP2718/EIP1559; for fee gasPrice/gasLimit is used + TransactionMode_Legacy TransactionMode = 0 + // Enveloped transaction EIP2718 (with type 0x2), fee is according to EIP1559 (base fee, inclusion fee, ...) + TransactionMode_Enveloped TransactionMode = 1 ) // Enum value maps for TransactionMode. @@ -73,6 +76,8 @@ type Transaction struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Payload transfer + // // Types that are assignable to TransactionOneof: // *Transaction_Transfer_ // *Transaction_Erc20Transfer @@ -211,28 +216,29 @@ type SigningInput struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Chain identifier (256-bit number) + // Chain identifier (uint256, serialized little endian) ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) Nonce []byte `protobuf:"bytes,2,opt,name=nonce,proto3" json:"nonce,omitempty"` // Transaction version selector: Legacy or enveloped, has impact on fee structure. // Default is Legacy (value 0) TxMode TransactionMode `protobuf:"varint,3,opt,name=tx_mode,json=txMode,proto3,enum=TW.Ethereum.Proto.TransactionMode" json:"tx_mode,omitempty"` - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) // Relevant for legacy transactions only (disregarded for enveloped/EIP1559) GasPrice []byte `protobuf:"bytes,4,opt,name=gas_price,json=gasPrice,proto3" json:"gas_price,omitempty"` - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) GasLimit []byte `protobuf:"bytes,5,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` - // Maxinmum optional inclusion fee (aka tip) (256-bit number) + // Maximum optional inclusion fee (aka tip) (uint256, serialized little endian) // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) MaxInclusionFeePerGas []byte `protobuf:"bytes,6,opt,name=max_inclusion_fee_per_gas,json=maxInclusionFeePerGas,proto3" json:"max_inclusion_fee_per_gas,omitempty"` - // Maxinmum fee (256-bit number) + // Maximum fee (uint256, serialized little endian) // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) MaxFeePerGas []byte `protobuf:"bytes,7,opt,name=max_fee_per_gas,json=maxFeePerGas,proto3" json:"max_fee_per_gas,omitempty"` // Recipient's address. ToAddress string `protobuf:"bytes,8,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` - // Private key. - PrivateKey []byte `protobuf:"bytes,9,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + // The secret private key used for signing (32 bytes). + PrivateKey []byte `protobuf:"bytes,9,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + // The payload transaction Transaction *Transaction `protobuf:"bytes,10,opt,name=transaction,proto3" json:"transaction,omitempty"` } @@ -338,7 +344,7 @@ func (x *SigningInput) GetTransaction() *Transaction { return nil } -// Transaction signing output. +// Result containing the signed and encoded transaction. type SigningOutput struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -346,14 +352,15 @@ type SigningOutput struct { // Signed and encoded transaction bytes. Encoded []byte `protobuf:"bytes,1,opt,name=encoded,proto3" json:"encoded,omitempty"` - V []byte `protobuf:"bytes,2,opt,name=v,proto3" json:"v,omitempty"` - R []byte `protobuf:"bytes,3,opt,name=r,proto3" json:"r,omitempty"` - S []byte `protobuf:"bytes,4,opt,name=s,proto3" json:"s,omitempty"` + // The V, R, S components of the resulting signature, (each uint256, serialized little endian) + V []byte `protobuf:"bytes,2,opt,name=v,proto3" json:"v,omitempty"` + R []byte `protobuf:"bytes,3,opt,name=r,proto3" json:"r,omitempty"` + S []byte `protobuf:"bytes,4,opt,name=s,proto3" json:"s,omitempty"` // The payload part, supplied in the input or assembled from input parameters Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` - /// error code, 0 is ok, other codes will be treated as errors + // error code, 0 is ok, other codes will be treated as errors Error common.SigningError `protobuf:"varint,6,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` - /// error code description + // error code description ErrorMessage string `protobuf:"bytes,7,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` } @@ -444,7 +451,7 @@ type Transaction_Transfer struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) Amount []byte `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` // Optional payload data Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` @@ -502,8 +509,9 @@ type Transaction_ERC20Transfer struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // destination address (string) To string `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) Amount []byte `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` } @@ -559,8 +567,9 @@ type Transaction_ERC20Approve struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Target of the approval Spender string `protobuf:"bytes,1,opt,name=spender,proto3" json:"spender,omitempty"` - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) Amount []byte `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` } @@ -616,9 +625,11 @@ type Transaction_ERC721Transfer struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Source address From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` - To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` - // ID of the token (256-bit number) + // Destination address + To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` + // ID of the token (uint256, serialized little endian) TokenId []byte `protobuf:"bytes,3,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` } @@ -681,11 +692,13 @@ type Transaction_ERC1155Transfer struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Source address From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` - To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` - // ID of the token (256-bit number) + // Destination address + To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` + // ID of the token (uint256, serialized little endian) TokenId []byte `protobuf:"bytes,3,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` - // The amount of tokens being transferred + // The amount of tokens being transferred (uint256, serialized little endian) Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` } @@ -763,7 +776,7 @@ type Transaction_ContractGeneric struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) Amount []byte `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` // Contract call payload data Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` @@ -924,13 +937,9 @@ var file_Ethereum_proto_rawDesc = []byte{ 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, 0x2c, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x6e, - 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x64, 0x10, 0x01, 0x42, 0x57, 0x0a, 0x15, 0x77, 0x61, 0x6c, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x64, 0x10, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, 0x69, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, - 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/samples/go/protos/common/TransactionCompiler.pb.go b/samples/go/protos/transactioncompiler/TransactionCompiler.pb.go similarity index 87% rename from samples/go/protos/common/TransactionCompiler.pb.go rename to samples/go/protos/transactioncompiler/TransactionCompiler.pb.go index 8baf5d4f998..d71cf247ce1 100644 --- a/samples/go/protos/common/TransactionCompiler.pb.go +++ b/samples/go/protos/transactioncompiler/TransactionCompiler.pb.go @@ -1,16 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.19.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.5 // source: TransactionCompiler.proto -package common +package transactioncompiler import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + common "tw/protos/common" ) const ( @@ -31,7 +32,7 @@ type PreSigningOutput struct { /// Pre-image data Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` /// error code, 0 is ok, other codes will be treated as errors - Error SigningError `protobuf:"varint,3,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` + Error common.SigningError `protobuf:"varint,3,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` /// error code description ErrorMessage string `protobuf:"bytes,4,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` } @@ -82,11 +83,11 @@ func (x *PreSigningOutput) GetData() []byte { return nil } -func (x *PreSigningOutput) GetError() SigningError { +func (x *PreSigningOutput) GetError() common.SigningError { if x != nil { return x.Error } - return SigningError_OK + return common.SigningError(0) } func (x *PreSigningOutput) GetErrorMessage() string { @@ -112,13 +113,9 @@ var file_TransactionCompiler_proto_rawDesc = []byte{ 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x55, + 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x17, 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, - 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -136,7 +133,7 @@ func file_TransactionCompiler_proto_rawDescGZIP() []byte { var file_TransactionCompiler_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_TransactionCompiler_proto_goTypes = []interface{}{ (*PreSigningOutput)(nil), // 0: TW.TxCompiler.Proto.PreSigningOutput - (SigningError)(0), // 1: TW.Common.Proto.SigningError + (common.SigningError)(0), // 1: TW.Common.Proto.SigningError } var file_TransactionCompiler_proto_depIdxs = []int32{ 1, // 0: TW.TxCompiler.Proto.PreSigningOutput.error:type_name -> TW.Common.Proto.SigningError @@ -152,7 +149,6 @@ func file_TransactionCompiler_proto_init() { if File_TransactionCompiler_proto != nil { return } - file_Common_proto_init() if !protoimpl.UnsafeEnabled { file_TransactionCompiler_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PreSigningOutput); i { diff --git a/samples/go/sample/external_signing.go b/samples/go/sample/external_signing.go index 636f56dbc0d..87b5b96d6b3 100644 --- a/samples/go/sample/external_signing.go +++ b/samples/go/sample/external_signing.go @@ -7,8 +7,8 @@ import ( "tw/core" "tw/protos/binance" "tw/protos/bitcoin" - "tw/protos/common" "tw/protos/ethereum" + "tw/protos/transactioncompiler" "google.golang.org/protobuf/proto" ) @@ -30,10 +30,10 @@ func SignExternalBinanceDemo() { coin, "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", // from "bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5", // to - "1", // amount - "BNB", // asset - "", // memo - "", // chainId + "1", // amount + "BNB", // asset + "", // memo + "", // chainId ) fmt.Println("txInputData len: ", len(txInputData)) @@ -41,7 +41,7 @@ func SignExternalBinanceDemo() { hashes := core.PreImageHashes(coin, txInputData) fmt.Println("hash(es): ", len(hashes), hex.EncodeToString(hashes)) - var preSigningOutput common.PreSigningOutput + var preSigningOutput transactioncompiler.PreSigningOutput proto.Unmarshal(hashes, &preSigningOutput) fmt.Println("\n==> Step 3: Compile transaction info") @@ -93,7 +93,7 @@ func SignExternalEthereumDemo() { hashes := core.PreImageHashes(coin, txInputData2) fmt.Println("hash(es): ", len(hashes), hex.EncodeToString(hashes)) - var preSigningOutput common.PreSigningOutput + var preSigningOutput transactioncompiler.PreSigningOutput proto.Unmarshal(hashes, &preSigningOutput) fmt.Println("\n==> Step 3: Compile transaction info") diff --git a/samples/go/types/twdata.go b/samples/go/types/twdata.go index dcb7bdc95f8..5bc9fa90f77 100644 --- a/samples/go/types/twdata.go +++ b/samples/go/types/twdata.go @@ -1,7 +1,7 @@ package types // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include import "C" diff --git a/samples/go/types/twstring.go b/samples/go/types/twstring.go index 3c5b2668c64..b61c52c565a 100644 --- a/samples/go/types/twstring.go +++ b/samples/go/types/twstring.go @@ -1,7 +1,7 @@ package types // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/local/lib -L../../../build/trezor-crypto -lTrustWalletCore -lwallet_core_rs -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include import "C" diff --git a/samples/rust/src/build.rs b/samples/rust/src/build.rs index cb96ab2c9c9..4f98962f974 100644 --- a/samples/rust/src/build.rs +++ b/samples/rust/src/build.rs @@ -11,7 +11,7 @@ use std::path::Path; static WALLET_CORE_PROJECT_DIR: &str = "../.."; // libs to link with, in reverse dependency order -static LIBS: [&str; 3] = ["TrustWalletCore", "TrezorCrypto", "protobuf"]; +static LIBS: [&str; 4] = ["TrustWalletCore", "TrezorCrypto", "protobuf", "wallet_core_rs"]; fn main() { // Generate protobuf interface files @@ -33,6 +33,7 @@ fn main() { println!("cargo:rustc-link-search=native={}/build", WALLET_CORE_PROJECT_DIR); println!("cargo:rustc-link-search=native={}/build/trezor-crypto", WALLET_CORE_PROJECT_DIR); + println!("cargo:rustc-link-search=native={}/build/local/lib", WALLET_CORE_PROJECT_DIR); // Libraries; order matters for i in 0..LIBS.len() { diff --git a/src/Aptos/Signer.cpp b/src/Aptos/Signer.cpp index 9c15ea200af..9ee223e5601 100644 --- a/src/Aptos/Signer.cpp +++ b/src/Aptos/Signer.cpp @@ -131,27 +131,38 @@ TransactionPayload registerTokenPayload(const Proto::SigningInput& input) { Proto::SigningOutput blindSign(const Proto::SigningInput& input) { auto output = Proto::SigningOutput(); - BCS::Serializer serializer; - auto encodedCall = parse_hex(input.any_encoded()); - serializer.add_bytes(begin(encodedCall), end(encodedCall)); auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto signature = privateKey.sign(encodedCall, TWCurveED25519); auto pubKeyData = privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes; - output.set_raw_txn(encodedCall.data(), encodedCall.size()); - output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); - output.mutable_authenticator()->set_signature(signature.data(), signature.size()); - serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; - output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); - - // clang-format off - nlohmann::json json = { - {"type", "ed25519_signature"}, - {"public_key", hexEncoded(pubKeyData)}, - {"signature", hexEncoded(signature)} - }; - // clang-format on - - output.set_json(json.dump()); + if (nlohmann::json j = nlohmann::json::parse(input.any_encoded(), nullptr, false); j.is_discarded()) { + BCS::Serializer serializer; + auto encodedCall = parse_hex(input.any_encoded()); + serializer.add_bytes(begin(encodedCall), end(encodedCall)); + auto signature = privateKey.sign(encodedCall, TWCurveED25519); + output.set_raw_txn(encodedCall.data(), encodedCall.size()); + output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); + output.mutable_authenticator()->set_signature(signature.data(), signature.size()); + serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; + output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); + + // clang-format off + nlohmann::json json = { + {"type", "ed25519_signature"}, + {"public_key", hexEncoded(pubKeyData)}, + {"signature", hexEncoded(signature)} + }; + // clang-format on + output.set_json(json.dump()); + } else { + TransactionBuilder::builder() + .sender(Address(input.sender())) + .sequenceNumber(input.sequence_number()) + .payload(EntryFunction::from_json(j)) + .maxGasAmount(input.max_gas_amount()) + .gasUnitPrice(input.gas_unit_price()) + .expirationTimestampSecs(input.expiration_timestamp_secs()) + .chainId(static_cast(input.chain_id())) + .sign(input, output); + } return output; } diff --git a/src/Aptos/TransactionPayload.cpp b/src/Aptos/TransactionPayload.cpp index 1e5b188e290..bc39b80866e 100644 --- a/src/Aptos/TransactionPayload.cpp +++ b/src/Aptos/TransactionPayload.cpp @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include "rust/bindgen/WalletCoreRSBindgen.h" #include #include @@ -54,4 +55,65 @@ nlohmann::json EntryFunction::json() const noexcept { return out; } +EntryFunction EntryFunction::from_json(const nlohmann::json& payload) noexcept { + auto splitFunctor = [](std::string s, std::string_view delimiter) { + size_t pos_start = 0, pos_end, delim_len = delimiter.size(); + std::string token; + std::vector output; + + while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + output.emplace_back(token); + } + + output.emplace_back(s.substr(pos_start)); + return output; + }; + auto functionSplitted = splitFunctor(payload.at("function").get(), "::"); + auto moduleId = ModuleId(Address(functionSplitted[0]), functionSplitted[1]); + std::vector args; + for (auto&& cur : payload.at("arguments")) { + auto curStr = cur.get(); + auto* res = parse_function_argument_to_bcs(curStr.c_str()); + args.emplace_back(parse_hex(res)); + free_string(res); + } + + std::vector tags; + + for (auto&& cur : payload.at("type_arguments")) { + auto curStr = cur.get(); + switch (parse_type_tag(curStr.c_str())) { + case ETypeTag::Bool: + break; + case ETypeTag::U8: + break; + case ETypeTag::U64: + break; + case ETypeTag::U128: + break; + case ETypeTag::Address: + break; + case ETypeTag::Signer: + break; + case ETypeTag::Vector: + break; + case ETypeTag::Struct: { + auto structSplitted = splitFunctor(curStr, "::"); + auto addr = Address(structSplitted[0]); + TypeTag tag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(addr, structSplitted[1], structSplitted[2], {})})}; + tags.emplace_back(tag); + break; + } + case ETypeTag::Error: + break; + default: + break; + } + } + + return EntryFunction(moduleId, functionSplitted[2], tags, {args}, payload.at("arguments")); +} + } // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.h b/src/Aptos/TransactionPayload.h index e52124f84ff..d6c453f995c 100644 --- a/src/Aptos/TransactionPayload.h +++ b/src/Aptos/TransactionPayload.h @@ -21,6 +21,7 @@ class EntryFunction { [[nodiscard]] const std::vector& tyArgs() const noexcept { return mTyArgs; } [[nodiscard]] const std::vector& args() const noexcept { return mArgs; } [[nodiscard]] nlohmann::json json() const noexcept; + static EntryFunction from_json(const nlohmann::json& json) noexcept; private: ModuleId mModule; diff --git a/src/rust/bindgen/.gitignore b/src/rust/bindgen/.gitignore new file mode 100644 index 00000000000..424c745c125 --- /dev/null +++ b/src/rust/bindgen/.gitignore @@ -0,0 +1 @@ +*.h diff --git a/swift/.gitignore b/swift/.gitignore index 7166ea4da50..f392a48ec06 100644 --- a/swift/.gitignore +++ b/swift/.gitignore @@ -14,3 +14,4 @@ cpp.xcconfig # fastlane fastlane/README.md fastlane/report.xml +WalletCoreRs.xcframework diff --git a/swift/Tests/Blockchains/AptosTests.swift b/swift/Tests/Blockchains/AptosTests.swift index 3c54581dafe..66f4b33ae48 100644 --- a/swift/Tests/Blockchains/AptosTests.swift +++ b/swift/Tests/Blockchains/AptosTests.swift @@ -19,6 +19,42 @@ class AptosTests: XCTestCase { XCTAssertNil(AnyAddress(string: invalid, coin: .aptos)) XCTAssertFalse(AnyAddress.isValid(string: invalid, coin: .aptos)) } + + func testBlindSign() { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x7efd69e7f9462774b932ce500ab51c0d0dcc004cf272e09f8ffd5804c2a84e33?network=mainnet + let payloadJson = """ + { + "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", + "type_arguments": [ + "0x1::aptos_coin::AptosCoin", + "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" + ], + "arguments": ["1000000", "49329"], + "type": "entry_function_payload" + } + """ + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet + let privateKeyData = Data(hexString: "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")! + let input = AptosSigningInput.with { + $0.chainID = 1 + $0.anyEncoded = payloadJson + $0.expirationTimestampSecs = 3664390082 + $0.gasUnitPrice = 100 + $0.maxGasAmount = 100011 + $0.sequenceNumber = 42 + $0.sender = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + $0.privateKey = privateKeyData + } + let output: AptosSigningOutput = AnySigner.sign(input: input, coin: .aptos) + let expectedRawTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada0000000001" + let expectedSignature = "42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b" + let expectedSignedTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c4042cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b" + XCTAssertEqual(output.rawTxn.hexString, expectedRawTx) + XCTAssertEqual(output.authenticator.signature.hexString, expectedSignature) + XCTAssertEqual(output.encoded.hexString, expectedSignedTx) + } func testSign() { // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet diff --git a/swift/common-xcframework.yml b/swift/common-xcframework.yml index fead1284c2c..dae0d4b55c7 100644 --- a/swift/common-xcframework.yml +++ b/swift/common-xcframework.yml @@ -8,7 +8,6 @@ options: macOS: 10.14 settings: base: - ENABLE_BITCODE: YES HEADER_SEARCH_PATHS: $(SRCROOT)/wallet-core ${SRCROOT}/trezor-crypto/crypto SYSTEM_HEADER_SEARCH_PATHS: ${SRCROOT}/include ${SRCROOT}/../build/local/include ${SRCROOT}/trezor-crypto/include $(SRCROOT)/protobuf /usr/local/include /opt/homebrew/include CLANG_CXX_LANGUAGE_STANDARD: c++20 @@ -35,12 +34,15 @@ targets: - "proto/*.proto" - "Tron/Protobuf/*.proto" - "Zilliqa/Protobuf/*.proto" + - "Cosmos/Protobuf/*.proto" + - "Hedera/Protobuf/*.proto" dependencies: - target: trezor-crypto_${platform} link: true - target: protobuf_${platform} link: true - sdk: libc++.tbd + - framework: WalletCoreRs.xcframework settings: SKIP_INSTALL: false SUPPORTS_MACCATALYST: true diff --git a/swift/fastlane/Fastfile b/swift/fastlane/Fastfile index 54bb2511daf..298bcb35a56 100644 --- a/swift/fastlane/Fastfile +++ b/swift/fastlane/Fastfile @@ -14,7 +14,8 @@ platform :ios do workspace: 'TrustWalletCore.xcworkspace', scheme: 'WalletCore', destinations: ['iOS', 'macOS'], - xcframework_output_directory: 'build' + xcframework_output_directory: 'build', + enable_bitcode: false ) end @@ -24,7 +25,8 @@ platform :ios do workspace: 'TrustWalletCore.xcworkspace', scheme: 'SwiftProtobuf', destinations: ['iOS', 'macOS'], - xcframework_output_directory: 'build' + xcframework_output_directory: 'build', + enable_bitcode: false ) end end diff --git a/swift/project.yml b/swift/project.yml index 274533d6a6b..df46f6a29e5 100644 --- a/swift/project.yml +++ b/swift/project.yml @@ -27,9 +27,10 @@ targets: excludes: - ".vscode" - "proto/*.proto" - - "Cosmos/Protobuf/*.proto" - "Tron/Protobuf/*.proto" - "Zilliqa/Protobuf/*.proto" + - "Cosmos/Protobuf/*.proto" + - "Hedera/Protobuf/*.proto" - Sources dependencies: - target: trezor-crypto @@ -37,6 +38,7 @@ targets: - target: protobuf link: true - sdk: libc++.tbd + - framework: WalletCoreRs.xcframework scheme: testTargets: - WalletCoreTests diff --git a/tests/chains/Aptos/SignerTests.cpp b/tests/chains/Aptos/SignerTests.cpp index c47c05bc3e5..8b9bd0c7f03 100644 --- a/tests/chains/Aptos/SignerTests.cpp +++ b/tests/chains/Aptos/SignerTests.cpp @@ -244,6 +244,69 @@ TEST(AptosSigner, CreateAccount) { assertJSONEqual(expectedJson, parsedJson); } +TEST(AptosSigner, BlindSignFromJson) { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x7efd69e7f9462774b932ce500ab51c0d0dcc004cf272e09f8ffd5804c2a84e33?network=mainnet + auto payloadJson = R"( + { + "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", + "type_arguments": [ + "0x1::aptos_coin::AptosCoin", + "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" + ], + "arguments": [ + "1000000", + "49329" + ], + "type": "entry_function_payload" + })"_json; + Proto::SigningInput input; + input.set_sequence_number(42); + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_gas_unit_price(100); + input.set_max_gas_amount(100011); + input.set_expiration_timestamp_secs(3664390082); + input.set_any_encoded(payloadJson.dump()); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_chain_id(1); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada0000000001"); + ASSERT_EQ(hex(result.authenticator().signature()), "42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c4042cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b"); + nlohmann::json expectedJson = R"( +{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", + "type_arguments": [ + "0x1::aptos_coin::AptosCoin", + "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" + ], + "arguments": [ + "1000000", + "49329" + ], + "type": "entry_function_payload" + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "42", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", + "type": "ed25519_signature" + } +} + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + TEST(AptosSigner, BlindSign) { // successfully broadcasted https://explorer.aptoslabs.com/txn/0xd95857a9e644528708778a3a0a6e13986751944fca30eaac98853c1655de0422?network=Devnet // encoded submission with: diff --git a/tests/common/rust/bindgen/WalletCoreRsTests.cpp b/tests/common/rust/bindgen/WalletCoreRsTests.cpp new file mode 100644 index 00000000000..ce99263e0c1 --- /dev/null +++ b/tests/common/rust/bindgen/WalletCoreRsTests.cpp @@ -0,0 +1,16 @@ +// Copyright © 2017-2022 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 "rust/bindgen/WalletCoreRSBindgen.h" + +#include "gtest/gtest.h" + +TEST(RustBindgen, MoveParseFunctionArgument) { + std::string arg = "10000000"; + auto* result = parse_function_argument_to_bcs(arg.c_str()); + ASSERT_EQ(std::string(result), "8096980000000000"); + free_string(result); +} diff --git a/tools/generate-files b/tools/generate-files index e6d74be818e..70825c15aaa 100755 --- a/tools/generate-files +++ b/tools/generate-files @@ -52,6 +52,9 @@ codegen/bin/codegen # Convert doxygen comments to appropriate format tools/doxygen_convert_comments +# Generate rust bindgen +tools/rust-bindgen + # Generate Java, C++ and Swift Protobuf files if [ -x "$(command -v protoc-gen-swift)" ]; then "$PROTOC" -I=$PREFIX/include -I=src/proto --cpp_out=src/proto --java_out=lite:jni/java --swift_out=swift/Sources/Generated/Protobuf --swift_opt=Visibility=Public src/proto/*.proto @@ -79,3 +82,4 @@ if [ -x "$(command -v xcodegen)" ]; then else echo -e "\nWARNING: Skipped generating Xcode project because the xcodegen tool is not installed." fi + diff --git a/tools/install-rust-dependencies b/tools/install-rust-dependencies new file mode 100755 index 00000000000..b2356e561d6 --- /dev/null +++ b/tools/install-rust-dependencies @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e + +if [[ `uname` == "Darwin" ]]; then + rustup update + rustup toolchain install nightly + rustup default nightly + rustup toolchain install nightly-x86_64-apple-darwin + rustup toolchain install nightly-aarch64-apple-darwin + rustup component add rust-src --toolchain nightly-aarch64-apple-darwin + rustup component add rust-src --toolchain nightly-x86_64-apple-darwin +fi + +# Android +rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android +# iOS +rustup target add aarch64-apple-darwin x86_64-apple-darwin +# macOS +rustup target add x86_64-apple-ios aarch64-apple-ios-sim aarch64-apple-ios +# Wasm +rustup target add wasm32-unknown-emscripten + +cargo install cbindgen diff --git a/tools/install-sys-dependencies-linux b/tools/install-sys-dependencies-linux new file mode 100755 index 00000000000..b9173e25e42 --- /dev/null +++ b/tools/install-sys-dependencies-linux @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e + + # build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake + sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev rustc --fix-missing diff --git a/tools/install-sys-dependencies-mac b/tools/install-sys-dependencies-mac new file mode 100755 index 00000000000..2e7fc69a00e --- /dev/null +++ b/tools/install-sys-dependencies-mac @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +brew install boost ninja xcodegen xcbeautify diff --git a/tools/ios-build b/tools/ios-build index e1b3945ecfc..8e12031289e 100755 --- a/tools/ios-build +++ b/tools/ios-build @@ -47,13 +47,14 @@ create_xc_framework() { xcodebuild -create-xcframework -output $BUILD_FOLDER/$FRAMEWORK.xcframework \ -framework $BUILD_FOLDER/ios-arm64.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ -framework $BUILD_FOLDER/ios-arm64_x86_64-simulator.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ + -framework $BUILD_FOLDER/ios-x86_64_arm64-maccatalyst.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ -framework $BUILD_FOLDER/macos-arm64_x86_64.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework } main() { init + build_mac_x64_arm64 && build_ios_mac_catalyst build_ios_arm64 && build_ios_simulator - build_mac_x64_arm64 create_xc_framework } diff --git a/tools/ios-doc b/tools/ios-doc index 9c004897a18..60c5a41b1cd 100755 --- a/tools/ios-doc +++ b/tools/ios-doc @@ -9,7 +9,7 @@ pushd swift mkdir -p build && rm -rf build/*.doccarchive export DOCC_JSON_PRETTYPRINT="YES" -xcodebuild -workspace TrustWalletCore.xcworkspace -derivedDataPath build/docsData -scheme WalletCore -destination 'platform=iOS Simulator,name=iPhone 13 Pro Max' -parallelizeTargets docbuild | xcbeautify +xcodebuild -workspace TrustWalletCore.xcworkspace -derivedDataPath build/docsData -scheme WalletCore -destination 'platform=iOS Simulator,name=iPhone 14' -parallelizeTargets docbuild | xcbeautify pushd build diff --git a/tools/rust-bindgen b/tools/rust-bindgen new file mode 100755 index 00000000000..ab885868d1c --- /dev/null +++ b/tools/rust-bindgen @@ -0,0 +1,43 @@ +#!/bin/bash + +TARGET_NAME="libwallet_core_rs.a" +TARGET_XCFRAMEWORK_NAME=../swift/WalletCoreRs.xcframework +BUILD_FOLDER=../build/local +CRATE="wallet-core-rs" +HEADER_NAME="WalletCoreRSBindgen.h" + +create_xc_framework() { + rm -rf $TARGET_XCFRAMEWORK_NAME + xcodebuild -create-xcframework -library $BUILD_FOLDER/$TARGET_NAME -library $BUILD_FOLDER/darwin_universal/$TARGET_NAME -library $BUILD_FOLDER/aarch64-apple-ios/release/$TARGET_NAME -library $BUILD_FOLDER/catalyst/$TARGET_NAME -output $TARGET_XCFRAMEWORK_NAME +} + +cd rust + +echo "Generating Native targets" +CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --release +CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target wasm32-unknown-emscripten --release +if [[ `uname` == "Darwin" ]]; then + echo "Generating Android targets" + CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --target aarch64-linux-android --release + CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --target armv7-linux-androideabi --release + CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --target x86_64-linux-android --release + CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --target i686-linux-android --release + echo "Generating iOS targets" + CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target aarch64-apple-ios --release + CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target aarch64-apple-ios-sim --release + CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target x86_64-apple-ios --release + CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target aarch64-apple-darwin --release + CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target x86_64-apple-darwin --release + CARGO_TARGET_DIR=$BUILD_FOLDER cargo +nightly build -Z build-std --target aarch64-apple-ios-macabi --release + CARGO_TARGET_DIR=$BUILD_FOLDER cargo +nightly build -Z build-std --target x86_64-apple-ios-macabi --release + lipo $BUILD_FOLDER/x86_64-apple-ios/release/$TARGET_NAME $BUILD_FOLDER/aarch64-apple-ios-sim/release/$TARGET_NAME -create -output $BUILD_FOLDER/$TARGET_NAME + mkdir -p $BUILD_FOLDER/darwin_universal + lipo $BUILD_FOLDER/x86_64-apple-darwin/release/$TARGET_NAME $BUILD_FOLDER/aarch64-apple-darwin/release/$TARGET_NAME -create -output $BUILD_FOLDER/darwin_universal/$TARGET_NAME + mkdir -p $BUILD_FOLDER/catalyst + lipo $BUILD_FOLDER/aarch64-apple-ios-macabi/release/$TARGET_NAME $BUILD_FOLDER/x86_64-apple-ios-macabi/release/$TARGET_NAME -create -output $BUILD_FOLDER/catalyst/$TARGET_NAME + + create_xc_framework +fi +cbindgen --crate $CRATE --output ../src/rust/bindgen/$HEADER_NAME +cd - +cp build/local/release/${TARGET_NAME} build/local/lib/ From 933e79018bcfa4fecc87ae73eadeac1f1a1c5341 Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Tue, 6 Dec 2022 07:30:44 +0000 Subject: [PATCH 058/426] fix(docs): fix error when uploading kotlin docs (#2781) --- tools/android-release | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/android-release b/tools/android-release index 4856fa5a5ec..e37796b2f6e 100755 --- a/tools/android-release +++ b/tools/android-release @@ -20,9 +20,13 @@ popd # android echo "Building docs..." tools/kotlin-doc +pushd build/dokka release_url=$(wc_release_url ${version}) echo "release_url url for docs is $release_url" -filename=build/dokka/kdoc.zip +filename=kdoc.zip download_url=$(wc_upload_asset ${release_url} ${filename}) echo "download_url is $download_url" + +popd # build/dokka + From 36d082788534f60ffe2cdc2e54bb83a32260b84a Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 6 Dec 2022 08:41:43 +0100 Subject: [PATCH 059/426] feat(aptos): add aptos staking/unstaking examples (#2780) --- tests/chains/Aptos/SignerTests.cpp | 103 +++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/tests/chains/Aptos/SignerTests.cpp b/tests/chains/Aptos/SignerTests.cpp index 8b9bd0c7f03..544240a7cfc 100644 --- a/tests/chains/Aptos/SignerTests.cpp +++ b/tests/chains/Aptos/SignerTests.cpp @@ -307,6 +307,109 @@ TEST(AptosSigner, BlindSignFromJson) { assertJSONEqual(expectedJson, parsedJson); } +TEST(AptosSigner, BlindSignStaking) { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/payload + auto payloadJson = R"( + { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + })"_json; + Proto::SigningInput input; + input.set_sequence_number(43); + input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); + input.set_gas_unit_price(100); + input.set_max_gas_amount(100011); + input.set_expiration_timestamp_secs(3664390082); + input.set_any_encoded(payloadJson.dump()); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_chain_id(1); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada0000000001"); + ASSERT_EQ(hex(result.authenticator().signature()), "a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b"); + ASSERT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b"); + nlohmann::json expectedJson = R"( +{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + }, + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "43", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xa41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", + "type": "ed25519_signature" + } +} + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, BlindSignUnStaking) { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968/payload + auto payloadJson = R"( + { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + })"_json; + Proto::SigningInput input; + input.set_sequence_number(44); + input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); + input.set_gas_unit_price(100); + input.set_max_gas_amount(100011); + input.set_expiration_timestamp_secs(3664390082); + input.set_any_encoded(payloadJson.dump()); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_chain_id(1); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada0000000001"); + ASSERT_EQ(hex(result.authenticator().signature()), "a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a"); + ASSERT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a"); + nlohmann::json expectedJson = R"( +{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + }, + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "44", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xa58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", + "type": "ed25519_signature" + } +} + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + + TEST(AptosSigner, BlindSign) { // successfully broadcasted https://explorer.aptoslabs.com/txn/0xd95857a9e644528708778a3a0a6e13986751944fca30eaac98853c1655de0422?network=Devnet // encoded submission with: From f50d771a99519e0723ac25f036c01969f909d710 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 8 Dec 2022 11:57:38 +0100 Subject: [PATCH 060/426] feat(build): update bootstrap.sh (#2787) --- bootstrap.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap.sh b/bootstrap.sh index 1dba588dd30..cdb20a25993 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -7,6 +7,7 @@ set -e echo "#### Initializing workspace with dependencies ... ####" tools/install-dependencies +tools/install-rust-dependencies echo "#### Building and running tests ... ####" tools/build-and-test From c33675119405a951ae2ce887d0a88e77a81c8fcf Mon Sep 17 00:00:00 2001 From: hewigovens <360470+hewigovens@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:34:22 +0900 Subject: [PATCH 061/426] Remove WC_ prefix (same with other actions) (#2793) --- android/trustwalletcore/maven-push.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/trustwalletcore/maven-push.gradle b/android/trustwalletcore/maven-push.gradle index 12746f09c4a..ba35797e7fe 100644 --- a/android/trustwalletcore/maven-push.gradle +++ b/android/trustwalletcore/maven-push.gradle @@ -43,8 +43,8 @@ publishing { name = "GitHubPackages" url = uri("https://maven.pkg.github.com/trustwallet/wallet-core") credentials { - username = System.getenv("WC_GITHUB_USER") - password = System.getenv("WC_GITHUB_TOKEN") + username = System.getenv("GITHUB_USER") + password = System.getenv("GITHUB_TOKEN") } } } From 89e337b1df8dcefc8a684578307a30e2e467bed7 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Tue, 13 Dec 2022 13:37:40 +0100 Subject: [PATCH 062/426] Unify Entry::deriveAddress() methods (#2796) --- codegen/lib/templates/newcoin/Entry.cpp.erb | 4 +-- codegen/lib/templates/newcoin/Entry.h.erb | 6 ++-- src/Aeternity/Entry.cpp | 2 +- src/Aeternity/Entry.h | 2 +- src/Aion/Entry.cpp | 2 +- src/Aion/Entry.h | 2 +- src/Algorand/Entry.cpp | 2 +- src/Algorand/Entry.h | 2 +- src/AnyAddress.cpp | 11 ++----- src/AnyAddress.h | 5 ++-- src/Aptos/Entry.cpp | 2 +- src/Aptos/Entry.h | 2 +- src/Binance/Entry.cpp | 2 +- src/Binance/Entry.h | 2 +- src/Bitcoin/Entry.cpp | 6 ++-- src/Bitcoin/Entry.h | 7 +---- src/Cardano/Entry.cpp | 2 +- src/Cardano/Entry.h | 2 +- src/Coin.cpp | 13 +-------- src/Coin.h | 8 ++---- src/CoinEntry.cpp | 32 +++++++++++++++++++++ src/CoinEntry.h | 19 ++++++------ src/Cosmos/Entry.cpp | 9 ++++-- src/Cosmos/Entry.h | 10 +++---- src/Decred/Entry.cpp | 2 +- src/Decred/Entry.h | 2 +- src/EOS/Entry.cpp | 2 +- src/EOS/Entry.h | 2 +- src/Elrond/Entry.cpp | 2 +- src/Elrond/Entry.h | 2 +- src/Ethereum/Entry.cpp | 2 +- src/Ethereum/Entry.h | 2 +- src/Everscale/Entry.cpp | 2 +- src/Everscale/Entry.h | 2 +- src/FIO/Entry.cpp | 2 +- src/FIO/Entry.h | 2 +- src/Filecoin/Entry.cpp | 3 +- src/Filecoin/Entry.h | 11 ++++--- src/Groestlcoin/Entry.cpp | 3 +- src/Groestlcoin/Entry.h | 6 ++-- src/Harmony/Entry.cpp | 2 +- src/Harmony/Entry.h | 2 +- src/Hedera/Entry.cpp | 2 +- src/Hedera/Entry.h | 2 +- src/Icon/Entry.cpp | 2 +- src/Icon/Entry.h | 2 +- src/IoTeX/Entry.cpp | 2 +- src/IoTeX/Entry.h | 2 +- src/Kusama/Entry.cpp | 2 +- src/Kusama/Entry.h | 2 +- src/NEAR/Entry.cpp | 2 +- src/NEAR/Entry.h | 2 +- src/NEO/Entry.cpp | 2 +- src/NEO/Entry.h | 2 +- src/NULS/Entry.cpp | 2 +- src/NULS/Entry.h | 2 +- src/Nano/Entry.cpp | 2 +- src/Nano/Entry.h | 2 +- src/Nebulas/Entry.cpp | 2 +- src/Nebulas/Entry.h | 2 +- src/Nervos/Entry.cpp | 4 +-- src/Nervos/Entry.h | 3 +- src/Nimiq/Entry.cpp | 2 +- src/Nimiq/Entry.h | 2 +- src/Oasis/Entry.cpp | 2 +- src/Oasis/Entry.h | 2 +- src/Ontology/Entry.cpp | 2 +- src/Ontology/Entry.h | 2 +- src/Polkadot/Entry.cpp | 12 +++----- src/Polkadot/Entry.h | 1 - src/Ronin/Entry.cpp | 2 +- src/Ronin/Entry.h | 2 +- src/Solana/Entry.cpp | 2 +- src/Solana/Entry.h | 2 +- src/Stellar/Entry.cpp | 2 +- src/Stellar/Entry.h | 2 +- src/Tezos/Entry.cpp | 2 +- src/Tezos/Entry.h | 2 +- src/Tron/Entry.cpp | 2 +- src/Tron/Entry.h | 2 +- src/Waves/Entry.cpp | 2 +- src/Waves/Entry.h | 2 +- src/XRP/Entry.cpp | 2 +- src/XRP/Entry.h | 2 +- src/Zcash/Entry.cpp | 3 +- src/Zcash/Entry.h | 2 +- src/Zilliqa/Entry.cpp | 2 +- src/Zilliqa/Entry.h | 2 +- src/interface/TWAnyAddress.cpp | 7 +++-- tests/common/AnyAddressTests.cpp | 4 +-- 90 files changed, 164 insertions(+), 157 deletions(-) create mode 100644 src/CoinEntry.cpp diff --git a/codegen/lib/templates/newcoin/Entry.cpp.erb b/codegen/lib/templates/newcoin/Entry.cpp.erb index a29ceb91951..6de5775f5b0 100644 --- a/codegen/lib/templates/newcoin/Entry.cpp.erb +++ b/codegen/lib/templates/newcoin/Entry.cpp.erb @@ -13,11 +13,11 @@ namespace TW::<%= format_name(coin) %> { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, [[maybe_unused]] const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } -std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/codegen/lib/templates/newcoin/Entry.h.erb b/codegen/lib/templates/newcoin/Entry.h.erb index 090d5d06445..52a1866eca6 100644 --- a/codegen/lib/templates/newcoin/Entry.h.erb +++ b/codegen/lib/templates/newcoin/Entry.h.erb @@ -14,9 +14,9 @@ namespace TW::<%= format_name(coin) %> { /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, const PrefixVariant& addressPrefix) 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; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; // normalizeAddress(): implement this if needed, e.g. Ethereum address is EIP55 checksummed // plan(): implement this if the blockchain is UTXO based }; diff --git a/src/Aeternity/Entry.cpp b/src/Aeternity/Entry.cpp index 0c1fcc95dce..391a248c057 100644 --- a/src/Aeternity/Entry.cpp +++ b/src/Aeternity/Entry.cpp @@ -18,7 +18,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Aeternity/Entry.h b/src/Aeternity/Entry.h index 211ffd707c2..748cd69b91c 100644 --- a/src/Aeternity/Entry.h +++ b/src/Aeternity/Entry.h @@ -15,7 +15,7 @@ namespace TW::Aeternity { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Aion/Entry.cpp b/src/Aion/Entry.cpp index 922e368f611..454fb6cbc05 100644 --- a/src/Aion/Entry.cpp +++ b/src/Aion/Entry.cpp @@ -20,7 +20,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Aion/Entry.h b/src/Aion/Entry.h index 551016bc95c..b09f3a4871f 100644 --- a/src/Aion/Entry.h +++ b/src/Aion/Entry.h @@ -15,7 +15,7 @@ namespace TW::Aion { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Algorand/Entry.cpp b/src/Algorand/Entry.cpp index 135434fc5cc..4eaa61977c7 100644 --- a/src/Algorand/Entry.cpp +++ b/src/Algorand/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Algorand/Entry.h b/src/Algorand/Entry.h index 7fa4a1f91a3..0f152c857cb 100644 --- a/src/Algorand/Entry.h +++ b/src/Algorand/Entry.h @@ -15,7 +15,7 @@ namespace TW::Algorand { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; diff --git a/src/AnyAddress.cpp b/src/AnyAddress.cpp index bf0fb994b98..6d20e19a15c 100644 --- a/src/AnyAddress.cpp +++ b/src/AnyAddress.cpp @@ -25,15 +25,8 @@ AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinTyp return new AnyAddress{.address = std::move(normalized), .coin = coin}; } -AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp, TWDerivation derivation) { - - auto derivedAddress = TW::deriveAddress(coin, publicKey, derivation, hrp); - return new AnyAddress{.address = std::move(derivedAddress), .coin = coin}; -} - -AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const PrefixVariant& addressPrefix, TWDerivation derivation) { - - auto derivedAddress = TW::deriveAddress(coin, publicKey, addressPrefix, derivation); +AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, TWDerivation derivation, const PrefixVariant& prefix) { + const auto derivedAddress = TW::deriveAddress(coin, publicKey, derivation, prefix); return new AnyAddress{.address = std::move(derivedAddress), .coin = coin}; } diff --git a/src/AnyAddress.h b/src/AnyAddress.h index e78f3d67a7c..0735fe24ceb 100644 --- a/src/AnyAddress.h +++ b/src/AnyAddress.h @@ -23,9 +23,10 @@ class AnyAddress { enum TWCoinType coin; + // Create address from string address and optional prefix; also normalizes the address. static AnyAddress* createAddress(const std::string& address, enum TWCoinType coin, const PrefixVariant& prefix = std::monostate()); - static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp = "", TWDerivation derivation = TWDerivationDefault); - static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const PrefixVariant& prefix, TWDerivation derivation = TWDerivationDefault); + // Create address from private key, with optional non-standard derivation and prefix + static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, TWDerivation derivation = TWDerivationDefault, const PrefixVariant& prefix = std::monostate()); Data getData() const; }; diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp index 44261fc64ac..4ecf7f053f6 100644 --- a/src/Aptos/Entry.cpp +++ b/src/Aptos/Entry.cpp @@ -15,7 +15,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Aptos/Entry.h b/src/Aptos/Entry.h index 3496e72e8ac..4f3f722643b 100644 --- a/src/Aptos/Entry.h +++ b/src/Aptos/Entry.h @@ -15,7 +15,7 @@ namespace TW::Aptos { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Binance/Entry.cpp b/src/Binance/Entry.cpp index a92d97c4a2d..f0e74088833 100644 --- a/src/Binance/Entry.cpp +++ b/src/Binance/Entry.cpp @@ -16,7 +16,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Binance/Entry.h b/src/Binance/Entry.h index ee8cd5b158b..3bcd1c91c89 100644 --- a/src/Binance/Entry.h +++ b/src/Binance/Entry.h @@ -15,7 +15,7 @@ namespace TW::Binance { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/Bitcoin/Entry.cpp b/src/Bitcoin/Entry.cpp index 861e239c61e..dd40b084e18 100644 --- a/src/Bitcoin/Entry.cpp +++ b/src/Bitcoin/Entry.cpp @@ -63,8 +63,10 @@ std::string Entry::normalizeAddress(TWCoinType coin, const std::string& address) } } -std::string Entry::deriveAddress(TWCoinType coin, TWDerivation derivation, const PublicKey& publicKey, - byte p2pkh, const char* hrp) const { +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { + byte p2pkh = getFromPrefixPkhOrDefault(addressPrefix, coin); + const char* hrp = getFromPrefixHrpOrDefault(addressPrefix, coin); + switch (coin) { case TWCoinTypeBitcoin: case TWCoinTypeLitecoin: diff --git a/src/Bitcoin/Entry.h b/src/Bitcoin/Entry.h index aec706f5d5f..b3b06d67707 100644 --- a/src/Bitcoin/Entry.h +++ b/src/Bitcoin/Entry.h @@ -17,12 +17,7 @@ class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, - const char* hrp) const { - return deriveAddress(coin, TWDerivationDefault, publicKey, p2pkh, hrp); - } - std::string deriveAddress(TWCoinType coin, TWDerivation derivation, const PublicKey& publicKey, - TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Cardano/Entry.cpp b/src/Cardano/Entry.cpp index 2bf09dde28d..df4c166faae 100644 --- a/src/Cardano/Entry.cpp +++ b/src/Cardano/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return AddressV3::isValidLegacy(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return AddressV3(publicKey).string(); } diff --git a/src/Cardano/Entry.h b/src/Cardano/Entry.h index 7cac8650256..04b55e259b8 100644 --- a/src/Cardano/Entry.h +++ b/src/Cardano/Entry.h @@ -15,7 +15,7 @@ namespace TW::Cardano { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Coin.cpp b/src/Coin.cpp index c99c086c8bc..dd82fc790d9 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -243,23 +243,12 @@ std::string TW::deriveAddress(TWCoinType coin, const PrivateKey& privateKey, TWD return TW::deriveAddress(coin, privateKey.getPublicKey(keyType), derivation); } -std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey, const PrefixVariant& addressPrefix, TWDerivation derivation) { +std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) { auto const* dispatcher = coinDispatcher(coin); assert(dispatcher != nullptr); return dispatcher->deriveAddress(coin, publicKey, derivation, addressPrefix); } -std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const std::string& hrp) { - auto p2pkh = TW::p2pkhPrefix(coin); - const char* hrpRaw = [&hrp, coin]() { - return hrp.empty() ? stringForHRP(TW::hrp(coin)) : hrp.c_str(); - }(); - // dispatch - auto* dispatcher = coinDispatcher(coin); - assert(dispatcher != nullptr); - return dispatcher->deriveAddress(coin, derivation, publicKey, p2pkh, hrpRaw); -} - Data TW::addressToData(TWCoinType coin, const std::string& address) { const auto* dispatcher = coinDispatcher(coin); assert(dispatcher != nullptr); diff --git a/src/Coin.h b/src/Coin.h index 3a994267d1b..a4920fe7419 100644 --- a/src/Coin.h +++ b/src/Coin.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -78,10 +78,8 @@ std::string deriveAddress(TWCoinType coin, const PrivateKey& privateKey); /// Derives the address for a particular coin from the private key, with given derivation. std::string deriveAddress(TWCoinType coin, const PrivateKey& privateKey, TWDerivation derivation); -/// Derives the address for a particular coin from the public key, with given derivation. -std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation = TWDerivationDefault, const std::string& hrp = ""); -/// Derives the address for a particular coin from the public key, with given derivation and explicit addressPrefix. -std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, const PrefixVariant& addressPrefix, TWDerivation derivation = TWDerivationDefault); +/// Derives the address for a particular coin from the public key, with given derivation and addressPrefix. +std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation = TWDerivationDefault, const PrefixVariant& addressPrefix = std::monostate()); /// Returns the binary representation of a string address Data addressToData(TWCoinType coin, const std::string& address); diff --git a/src/CoinEntry.cpp b/src/CoinEntry.cpp new file mode 100644 index 00000000000..e61da49ee62 --- /dev/null +++ b/src/CoinEntry.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 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 "CoinEntry.h" +#include "Coin.h" +#include + +namespace TW { + +const char* getFromPrefixHrpOrDefault(const PrefixVariant &prefix, TWCoinType coin) { + if (std::holds_alternative(prefix)) { + const char* fromPrefix = std::get(prefix); + if (fromPrefix != nullptr && *fromPrefix != 0) { + return fromPrefix; + } + } + // Prefix contains no hrp or empty, return coin-default + return stringForHRP(TW::hrp(coin)); +} + +byte getFromPrefixPkhOrDefault(const PrefixVariant &prefix, TWCoinType coin) { + if (std::holds_alternative(prefix)) { + return std::get(prefix).p2pkh; + } + // Prefix contains no base58 prefixes, return coin-default + return TW::p2pkhPrefix(coin); +} + +} // namespace TW diff --git a/src/CoinEntry.h b/src/CoinEntry.h index 99a56942ff8..5ecb055ddc6 100644 --- a/src/CoinEntry.h +++ b/src/CoinEntry.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -42,15 +42,8 @@ class CoinEntry { virtual bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const = 0; // normalizeAddress is optional, it may leave this default, no-change implementation virtual std::string normalizeAddress([[maybe_unused]] TWCoinType coin, const std::string& address) const { return address; } - // Address derivation, default derivation - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const = 0; - virtual std::string deriveAddress([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return ""; - }; - // Address derivation, by default invoking default - virtual std::string deriveAddress(TWCoinType coin, [[maybe_unused]] TWDerivation derivation, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const { - return deriveAddress(coin, publicKey, p2pkh, hrp); - } + // Address derivation + virtual std::string deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const = 0; // Return the binary representation of a string address, used by AnyAddress // It is optional, if not defined, 'AnyAddress' interface will not support this coin. virtual Data addressToData([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const std::string& address) const { return {}; } @@ -117,4 +110,10 @@ Data txCompilerTemplate(const Data& dataIn, Func&& fnHandler) { return TW::data(output.SerializeAsString()); } +// Get the hrp from the prefix variant, or the coin-default if it is empty or it is not an hrp +const char* getFromPrefixHrpOrDefault(const PrefixVariant &prefix, TWCoinType coin); + +// Get the p2pkh prefix from the prefix variant, or the coin-default if it does not contain base58 prefixes +byte getFromPrefixPkhOrDefault(const PrefixVariant &prefix, TWCoinType coin); + } // namespace TW diff --git a/src/Cosmos/Entry.cpp b/src/Cosmos/Entry.cpp index 7de1049e24e..8f6ee8ada61 100644 --- a/src/Cosmos/Entry.cpp +++ b/src/Cosmos/Entry.cpp @@ -26,9 +26,12 @@ bool Entry::validateAddress(TWCoinType coin, const std::string& address, const P return false; } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char* hrp) const { - if (!std::string(hrp).empty()) { - return Address(hrp, publicKey, coin).string(); +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { + if (std::holds_alternative(addressPrefix)) { + const std::string hrp = std::get(addressPrefix); + if (!hrp.empty()) { + return Address(hrp, publicKey, coin).string(); + } } return Address(coin, publicKey).string(); } diff --git a/src/Cosmos/Entry.h b/src/Cosmos/Entry.h index 599bb623f01..a77f6104b82 100644 --- a/src/Cosmos/Entry.h +++ b/src/Cosmos/Entry.h @@ -15,11 +15,11 @@ namespace TW::Cosmos { class Entry : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const final; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const final; - Data addressToData(TWCoinType coin, const std::string& address) const final; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; - bool supportsJSONSigning() const final { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; + Data addressToData(TWCoinType coin, const std::string& address) const final; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; + bool supportsJSONSigning() const final { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; }; } // namespace TW::Cosmos diff --git a/src/Decred/Entry.cpp b/src/Decred/Entry.cpp index 36e0b21d1e7..778ec5410cc 100644 --- a/src/Decred/Entry.cpp +++ b/src/Decred/Entry.cpp @@ -15,7 +15,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] const char* hrp) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Decred/Entry.h b/src/Decred/Entry.h index d6d2dccb262..af3d8ab9784 100644 --- a/src/Decred/Entry.h +++ b/src/Decred/Entry.h @@ -15,7 +15,7 @@ namespace TW::Decred { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/EOS/Entry.cpp b/src/EOS/Entry.cpp index f2ecbbfa138..efb9f53be96 100644 --- a/src/EOS/Entry.cpp +++ b/src/EOS/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/EOS/Entry.h b/src/EOS/Entry.h index 3d5c1fda040..49fea13f207 100644 --- a/src/EOS/Entry.h +++ b/src/EOS/Entry.h @@ -15,7 +15,7 @@ namespace TW::EOS { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Elrond/Entry.cpp b/src/Elrond/Entry.cpp index ff04f37b8ed..6cd504b6008 100644 --- a/src/Elrond/Entry.cpp +++ b/src/Elrond/Entry.cpp @@ -19,7 +19,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Elrond/Entry.h b/src/Elrond/Entry.h index 27b1270a462..df2a0967e7f 100644 --- a/src/Elrond/Entry.h +++ b/src/Elrond/Entry.h @@ -15,7 +15,7 @@ namespace TW::Elrond { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/Ethereum/Entry.cpp b/src/Ethereum/Entry.cpp index 789384a4068..b862da36606 100644 --- a/src/Ethereum/Entry.cpp +++ b/src/Ethereum/Entry.cpp @@ -24,7 +24,7 @@ string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& a return Address(address).string(); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Ethereum/Entry.h b/src/Ethereum/Entry.h index b92042e91dc..f9a053992c5 100644 --- a/src/Ethereum/Entry.h +++ b/src/Ethereum/Entry.h @@ -16,7 +16,7 @@ class Entry : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const final; std::string normalizeAddress(TWCoinType coin, const std::string& address) const final; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const final; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const final; Data addressToData(TWCoinType coin, const std::string& address) const final; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; bool supportsJSONSigning() const final { return true; } diff --git a/src/Everscale/Entry.cpp b/src/Everscale/Entry.cpp index 216cca93ec4..68cd990d2f1 100644 --- a/src/Everscale/Entry.cpp +++ b/src/Everscale/Entry.cpp @@ -25,7 +25,7 @@ string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& a return Address(address).string(); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey, WorkchainType::Basechain).string(); } diff --git a/src/Everscale/Entry.h b/src/Everscale/Entry.h index d24d55ebceb..c0adfb3fe0a 100644 --- a/src/Everscale/Entry.h +++ b/src/Everscale/Entry.h @@ -16,7 +16,7 @@ class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* d) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/FIO/Entry.cpp b/src/FIO/Entry.cpp index c435b98ff4f..42111a0848b 100644 --- a/src/FIO/Entry.cpp +++ b/src/FIO/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/FIO/Entry.h b/src/FIO/Entry.h index 49fa7e95002..98f0fab9106 100644 --- a/src/FIO/Entry.h +++ b/src/FIO/Entry.h @@ -15,7 +15,7 @@ namespace TW::FIO { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Filecoin/Entry.cpp b/src/Filecoin/Entry.cpp index 0317621d6a4..07696a24cb7 100644 --- a/src/Filecoin/Entry.cpp +++ b/src/Filecoin/Entry.cpp @@ -17,8 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, - const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Filecoin/Entry.h b/src/Filecoin/Entry.h index 7db1f33b6b0..57e9f71f111 100644 --- a/src/Filecoin/Entry.h +++ b/src/Filecoin/Entry.h @@ -15,12 +15,11 @@ namespace TW::Filecoin { /// includes in this file class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, - const char* hrp) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - bool supportsJSONSigning() const { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Filecoin diff --git a/src/Groestlcoin/Entry.cpp b/src/Groestlcoin/Entry.cpp index 6235c416271..4886146bc36 100644 --- a/src/Groestlcoin/Entry.cpp +++ b/src/Groestlcoin/Entry.cpp @@ -19,7 +19,8 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return TW::Bitcoin::SegwitAddress::isValid(address, std::get(addressPrefix)); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TW::byte p2pkh, const char* hrp) const { +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { + std::string hrp = getFromPrefixHrpOrDefault(addressPrefix, coin); return TW::Bitcoin::SegwitAddress(publicKey, hrp).string(); } diff --git a/src/Groestlcoin/Entry.h b/src/Groestlcoin/Entry.h index 37ea8ed1bc2..00e97274de9 100644 --- a/src/Groestlcoin/Entry.h +++ b/src/Groestlcoin/Entry.h @@ -15,9 +15,9 @@ namespace TW::Groestlcoin { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Groestlcoin diff --git a/src/Harmony/Entry.cpp b/src/Harmony/Entry.cpp index 750f4d735ab..ccfd90d67f4 100644 --- a/src/Harmony/Entry.cpp +++ b/src/Harmony/Entry.cpp @@ -20,7 +20,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Harmony/Entry.h b/src/Harmony/Entry.h index 167779f8292..decd33bdaab 100644 --- a/src/Harmony/Entry.h +++ b/src/Harmony/Entry.h @@ -15,7 +15,7 @@ namespace TW::Harmony { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/Hedera/Entry.cpp b/src/Hedera/Entry.cpp index 60dfc7e124a..06691828d7c 100644 --- a/src/Hedera/Entry.cpp +++ b/src/Hedera/Entry.cpp @@ -15,7 +15,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Hedera/Entry.h b/src/Hedera/Entry.h index 5cf496ef185..ec85355b1c0 100644 --- a/src/Hedera/Entry.h +++ b/src/Hedera/Entry.h @@ -15,7 +15,7 @@ namespace TW::Hedera { class Entry final : public CoinEntry { public: virtual bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Icon/Entry.cpp b/src/Icon/Entry.cpp index 4df47c1728a..0f5572b8e9a 100644 --- a/src/Icon/Entry.cpp +++ b/src/Icon/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey, Icon::TypeAddress).string(); } diff --git a/src/Icon/Entry.h b/src/Icon/Entry.h index feeed6df284..05d155ec177 100644 --- a/src/Icon/Entry.h +++ b/src/Icon/Entry.h @@ -15,7 +15,7 @@ namespace TW::Icon { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/IoTeX/Entry.cpp b/src/IoTeX/Entry.cpp index 85db67fe256..560aa3dcca1 100644 --- a/src/IoTeX/Entry.cpp +++ b/src/IoTeX/Entry.cpp @@ -19,7 +19,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/IoTeX/Entry.h b/src/IoTeX/Entry.h index 8793f9bcab9..2a6442749c9 100644 --- a/src/IoTeX/Entry.h +++ b/src/IoTeX/Entry.h @@ -15,7 +15,7 @@ namespace TW::IoTeX { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Kusama/Entry.cpp b/src/Kusama/Entry.cpp index 98876e41124..987862b4453 100644 --- a/src/Kusama/Entry.cpp +++ b/src/Kusama/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Kusama/Entry.h b/src/Kusama/Entry.h index 0398c6f4acf..9104e6b514b 100644 --- a/src/Kusama/Entry.h +++ b/src/Kusama/Entry.h @@ -15,7 +15,7 @@ namespace TW::Kusama { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/NEAR/Entry.cpp b/src/NEAR/Entry.cpp index 63d69948969..17c60492305 100644 --- a/src/NEAR/Entry.cpp +++ b/src/NEAR/Entry.cpp @@ -24,7 +24,7 @@ string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& a return Address(address).string(); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/NEAR/Entry.h b/src/NEAR/Entry.h index 41c5f52fc20..7e1786dc70d 100644 --- a/src/NEAR/Entry.h +++ b/src/NEAR/Entry.h @@ -16,7 +16,7 @@ class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/NEO/Entry.cpp b/src/NEO/Entry.cpp index 27a5d53fd92..fb17923025b 100644 --- a/src/NEO/Entry.cpp +++ b/src/NEO/Entry.cpp @@ -18,7 +18,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] const char* hrp) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/NEO/Entry.h b/src/NEO/Entry.h index 8881a48a2ba..d250e9bf494 100644 --- a/src/NEO/Entry.h +++ b/src/NEO/Entry.h @@ -15,7 +15,7 @@ namespace TW::NEO { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/NULS/Entry.cpp b/src/NULS/Entry.cpp index 27aba6043d9..726bb87df27 100644 --- a/src/NULS/Entry.cpp +++ b/src/NULS/Entry.cpp @@ -19,7 +19,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/NULS/Entry.h b/src/NULS/Entry.h index 1060cc41c8e..7a295547af5 100644 --- a/src/NULS/Entry.h +++ b/src/NULS/Entry.h @@ -15,7 +15,7 @@ namespace TW::NULS { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Nano/Entry.cpp b/src/Nano/Entry.cpp index 86858b9b1d3..e8e9fc1ab77 100644 --- a/src/Nano/Entry.cpp +++ b/src/Nano/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Nano/Entry.h b/src/Nano/Entry.h index 9ca84e41eb0..50741817798 100644 --- a/src/Nano/Entry.h +++ b/src/Nano/Entry.h @@ -15,7 +15,7 @@ namespace TW::Nano { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/Nebulas/Entry.cpp b/src/Nebulas/Entry.cpp index 0487bb74e7d..fa0d26ce4ba 100644 --- a/src/Nebulas/Entry.cpp +++ b/src/Nebulas/Entry.cpp @@ -18,7 +18,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Nebulas/Entry.h b/src/Nebulas/Entry.h index 7fa88305c1c..da13dc54a1d 100644 --- a/src/Nebulas/Entry.h +++ b/src/Nebulas/Entry.h @@ -15,7 +15,7 @@ namespace TW::Nebulas { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Nervos/Entry.cpp b/src/Nervos/Entry.cpp index b4e9b071d55..48299735dc0 100644 --- a/src/Nervos/Entry.cpp +++ b/src/Nervos/Entry.cpp @@ -17,8 +17,8 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address, hrpPrefix ? *hrpPrefix : HRP_NERVOS); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, byte, - const char* hrp) const { +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { + const char* hrp = getFromPrefixHrpOrDefault(addressPrefix, coin); return Address(publicKey, hrp).string(); } diff --git a/src/Nervos/Entry.h b/src/Nervos/Entry.h index e91a777e7fa..6cd02079189 100644 --- a/src/Nervos/Entry.h +++ b/src/Nervos/Entry.h @@ -18,8 +18,7 @@ namespace TW::Nervos { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, byte p2pkh, - const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Nimiq/Entry.cpp b/src/Nimiq/Entry.cpp index c063fe2d977..e750d1524bd 100644 --- a/src/Nimiq/Entry.cpp +++ b/src/Nimiq/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Nimiq/Entry.h b/src/Nimiq/Entry.h index 6e19d3e24af..0a256d002fe 100644 --- a/src/Nimiq/Entry.h +++ b/src/Nimiq/Entry.h @@ -15,7 +15,7 @@ namespace TW::Nimiq { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Oasis/Entry.cpp b/src/Oasis/Entry.cpp index a9e1f4172bf..8d6f24bddd3 100644 --- a/src/Oasis/Entry.cpp +++ b/src/Oasis/Entry.cpp @@ -19,7 +19,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Oasis/Entry.h b/src/Oasis/Entry.h index 4ae19abbf0a..e8d1cf2af05 100644 --- a/src/Oasis/Entry.h +++ b/src/Oasis/Entry.h @@ -15,7 +15,7 @@ namespace TW::Oasis { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Ontology/Entry.cpp b/src/Ontology/Entry.cpp index 51d251e24d0..60d7da78a29 100644 --- a/src/Ontology/Entry.cpp +++ b/src/Ontology/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Ontology/Entry.h b/src/Ontology/Entry.h index 836a0f391e3..42a6ab1424e 100644 --- a/src/Ontology/Entry.h +++ b/src/Ontology/Entry.h @@ -15,7 +15,7 @@ namespace TW::Ontology { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Polkadot/Entry.cpp b/src/Polkadot/Entry.cpp index db51b204ec1..d14e3ed5887 100644 --- a/src/Polkadot/Entry.cpp +++ b/src/Polkadot/Entry.cpp @@ -20,7 +20,10 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { + if (auto* ss58Prefix = std::get_if(&addressPrefix); ss58Prefix) { + return Address(publicKey, *ss58Prefix).string(); + } return Address(publicKey).string(); } @@ -33,11 +36,4 @@ void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::D signTemplate(dataIn, dataOut); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { - if (auto* ss58Prefix = std::get_if(&addressPrefix); ss58Prefix) { - return Address(publicKey, *ss58Prefix).string(); - } - return ""; -} - } // namespace TW::Polkadot diff --git a/src/Polkadot/Entry.h b/src/Polkadot/Entry.h index 5c19d70c6db..d62be51e167 100644 --- a/src/Polkadot/Entry.h +++ b/src/Polkadot/Entry.h @@ -15,7 +15,6 @@ namespace TW::Polkadot { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Ronin/Entry.cpp b/src/Ronin/Entry.cpp index 6eaf45cf2d8..8ad42d7feb6 100644 --- a/src/Ronin/Entry.cpp +++ b/src/Ronin/Entry.cpp @@ -22,7 +22,7 @@ string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& a return Address(address).string(); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Ronin/Entry.h b/src/Ronin/Entry.h index 8723a549db1..1b485ea4cc1 100644 --- a/src/Ronin/Entry.h +++ b/src/Ronin/Entry.h @@ -15,7 +15,7 @@ class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/Solana/Entry.cpp b/src/Solana/Entry.cpp index 1e216d426ab..9ce90b67609 100644 --- a/src/Solana/Entry.cpp +++ b/src/Solana/Entry.cpp @@ -20,7 +20,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Solana/Entry.h b/src/Solana/Entry.h index 2735b866e69..3812f579ad9 100644 --- a/src/Solana/Entry.h +++ b/src/Solana/Entry.h @@ -15,7 +15,7 @@ namespace TW::Solana { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/Stellar/Entry.cpp b/src/Stellar/Entry.cpp index ce6cded2f72..f694d347c7f 100644 --- a/src/Stellar/Entry.cpp +++ b/src/Stellar/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Stellar/Entry.h b/src/Stellar/Entry.h index 14f9dc4b6cd..617aa3cac10 100644 --- a/src/Stellar/Entry.h +++ b/src/Stellar/Entry.h @@ -15,7 +15,7 @@ namespace TW::Stellar { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Tezos/Entry.cpp b/src/Tezos/Entry.cpp index 19a6dc9f66c..39120d30a1b 100644 --- a/src/Tezos/Entry.cpp +++ b/src/Tezos/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Tezos/Entry.h b/src/Tezos/Entry.h index ae744e64236..3b9f1bd697c 100644 --- a/src/Tezos/Entry.h +++ b/src/Tezos/Entry.h @@ -15,7 +15,7 @@ namespace TW::Tezos { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; diff --git a/src/Tron/Entry.cpp b/src/Tron/Entry.cpp index 8a0feb4d29b..393c6be330d 100644 --- a/src/Tron/Entry.cpp +++ b/src/Tron/Entry.cpp @@ -18,7 +18,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Tron/Entry.h b/src/Tron/Entry.h index 531f2ef9127..d689b890aa7 100644 --- a/src/Tron/Entry.h +++ b/src/Tron/Entry.h @@ -15,7 +15,7 @@ namespace TW::Tron { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Waves/Entry.cpp b/src/Waves/Entry.cpp index 30b5e730871..5e0e0580904 100644 --- a/src/Waves/Entry.cpp +++ b/src/Waves/Entry.cpp @@ -19,7 +19,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Waves/Entry.h b/src/Waves/Entry.h index fe30cd62120..68d760f6718 100644 --- a/src/Waves/Entry.h +++ b/src/Waves/Entry.h @@ -15,7 +15,7 @@ namespace TW::Waves { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/XRP/Entry.cpp b/src/XRP/Entry.cpp index 7fd7c10f85e..c916cdcfd82 100644 --- a/src/XRP/Entry.cpp +++ b/src/XRP/Entry.cpp @@ -18,7 +18,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address) || XAddress::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/XRP/Entry.h b/src/XRP/Entry.h index f30c07fb4bc..0e8f4d98d59 100644 --- a/src/XRP/Entry.h +++ b/src/XRP/Entry.h @@ -15,7 +15,7 @@ namespace TW::Ripple { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Zcash/Entry.cpp b/src/Zcash/Entry.cpp index 883cf768027..5e23b51d63c 100644 --- a/src/Zcash/Entry.cpp +++ b/src/Zcash/Entry.cpp @@ -15,7 +15,8 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return TAddress::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, [[maybe_unused]] const char* hrp) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { + byte p2pkh = getFromPrefixPkhOrDefault(addressPrefix, coin); return TAddress(publicKey, p2pkh).string(); } diff --git a/src/Zcash/Entry.h b/src/Zcash/Entry.h index c50bcfc8ea7..787597ba42e 100644 --- a/src/Zcash/Entry.h +++ b/src/Zcash/Entry.h @@ -15,7 +15,7 @@ namespace TW::Zcash { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/src/Zilliqa/Entry.cpp b/src/Zilliqa/Entry.cpp index 249c2e2b5b0..3d4a09a635d 100644 --- a/src/Zilliqa/Entry.cpp +++ b/src/Zilliqa/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& return Address::isValid(address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address(publicKey).string(); } diff --git a/src/Zilliqa/Entry.h b/src/Zilliqa/Entry.h index 90eda0931d1..a19932b711c 100644 --- a/src/Zilliqa/Entry.h +++ b/src/Zilliqa/Entry.h @@ -15,7 +15,7 @@ namespace TW::Zilliqa { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; bool supportsJSONSigning() const { return true; } diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index 4f4713679ed..aae4d7d1fbf 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -10,6 +10,7 @@ #include "Data.h" #include "Coin.h" +#include "CoinEntry.h" #include "AnyAddress.h" bool TWAnyAddressEqual(struct TWAnyAddress* _Nonnull lhs, struct TWAnyAddress* _Nonnull rhs) { @@ -70,17 +71,17 @@ struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKey( struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKeyDerivation( struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, enum TWDerivation derivation) { - return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, std::string(""), derivation)}; + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, derivation)}; } struct TWAnyAddress* _Nonnull TWAnyAddressCreateBech32WithPublicKey( struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, TWString* _Nonnull hrp) { const auto& hrpStr = *reinterpret_cast(hrp); - return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, hrpStr)}; + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, TWDerivationDefault, TW::Bech32Prefix(hrpStr.c_str()))}; } struct TWAnyAddress* TWAnyAddressCreateSS58WithPublicKey(struct TWPublicKey* publicKey, enum TWCoinType coin, uint32_t ss58Prefix) { - return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, ss58Prefix)}; + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, TWDerivationDefault, TW::SS58Prefix(ss58Prefix))}; } void TWAnyAddressDelete(struct TWAnyAddress* _Nonnull address) { diff --git a/tests/common/AnyAddressTests.cpp b/tests/common/AnyAddressTests.cpp index 917bd0ca07d..9adc700a2a1 100644 --- a/tests/common/AnyAddressTests.cpp +++ b/tests/common/AnyAddressTests.cpp @@ -30,11 +30,11 @@ TEST(AnyAddress, createFromPubKeyDerivation) { const Data key = parse_hex(ANY_ADDRESS_TEST_PUBKEY); PublicKey publicKey(key, TWPublicKeyTypeSECP256k1); { - std::unique_ptr addr(AnyAddress::createAddress(publicKey, TWCoinTypeBitcoin, std::string(""), TWDerivationDefault)); + std::unique_ptr addr(AnyAddress::createAddress(publicKey, TWCoinTypeBitcoin, TWDerivationDefault, std::monostate())); EXPECT_EQ(addr->address, ANY_ADDRESS_TEST_ADDRESS); } { - std::unique_ptr addr(AnyAddress::createAddress(publicKey, TWCoinTypeBitcoin, std::string(""), TWDerivationBitcoinLegacy)); + std::unique_ptr addr(AnyAddress::createAddress(publicKey, TWCoinTypeBitcoin, TWDerivationBitcoinLegacy, std::monostate())); EXPECT_EQ(addr->address, "1JvRfEQFv5q5qy9uTSAezH7kVQf4hqnHXx"); } } From 501f683b2b8301bc027b99a1ceaed1e104f70a2f Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Thu, 15 Dec 2022 08:41:08 +0100 Subject: [PATCH 063/426] =?UTF-8?q?New=20=E2=80=98newevmchain=E2=80=99=20s?= =?UTF-8?q?cript,=20subset=20of=20=E2=80=98newcoin=E2=80=99=20(#2792)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- codegen/bin/newcoin | 134 +---------------------------- codegen/bin/newevmchain | 21 +++++ codegen/lib/coin_skeleton_gen.rb | 142 +++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 130 deletions(-) create mode 100755 codegen/bin/newevmchain create mode 100755 codegen/lib/coin_skeleton_gen.rb diff --git a/codegen/bin/newcoin b/codegen/bin/newcoin index 8fdacb210fe..aeeec609d73 100755 --- a/codegen/bin/newcoin +++ b/codegen/bin/newcoin @@ -1,96 +1,14 @@ #!/usr/bin/env ruby -# Sript for creating new skeleton files for a new coin -# 1. Add relevsant entry to registry.json (in order to minimize merge conflict, don't add at the very end) +# Sript for creating new skeleton files for a new coin. See also `newevmchain`. +# 1. Add relevant entry to registry.json (in order to minimize merge conflict, don't add at the very end) # 2. Invoke this script with the id of the coin, e.g.: codegen/bin/newcoin ethereum -require 'erb' require 'fileutils' -require 'json' CurrentDir = File.dirname(__FILE__) $LOAD_PATH.unshift(File.join(CurrentDir, '..', 'lib')) -require 'entity_decl' -require 'code_generator' -require 'coin_test_gen' - -$flag_comment = " // TODO remove if the blockchain already exists, or just remove this comment if not" - -# Transforms a coin name to a C++ name -def self.format_name(coin) - formatted = coin['name'] - formatted = formatted.gsub(/-/, ' ') - formatted = formatted.gsub(/\./, ' ') - formatted = formatted.gsub(/\s/, '') - formatted -end - -def self.format_name_lowercase(coin) - format_name(coin).downcase -end - -def self.format_name_uppercase(coin) - format_name(coin).upcase -end - -def self.generate_file(templateFile, folder, fileName, coin) - @coin = coin - name = format_name(coin) - path = File.expand_path(templateFile, File.join(File.dirname(__FILE__), '..', 'lib', 'templates')) - template = ERB.new(File.read(path), nil, '-') - result = template.result(binding) - - FileUtils.mkdir_p folder - path = File.join(folder, fileName) - File.write(path, result) - puts "Generated file " + path -end - -def self.insert_coin_type(coin) - target_file = "include/TrustWalletCore/TWCoinType.h" - target_line = " TWCoinType#{format_name(coin)} = #{coin['coinId']},\n" - if insert_target_line(target_file, target_line, "};\n") - insert_blockchain_type(coin) - end -end - -def insert_blockchain_type(coin) - target_file = "include/TrustWalletCore/TWBlockchain.h" - line_number = File.readlines(target_file).count + 2 # add offset because of removed blockchain enum type - target_line = " TWBlockchain#{coin['blockchain']} = #{line_number - 17}, " + $flag_comment + "\n" - insert_target_line(target_file, target_line, "};\n") -end - -def insert_coin_entry(coin) - target_file = "src/Coin.cpp" - entryName = coin['blockchain'] - target_line = "#include \"#{entryName}/Entry.h\"" + $flag_comment + "\n" - insert_target_line(target_file, target_line, "// end_of_coin_includes_marker_do_not_modify\n") - target_line = "#{entryName}::Entry #{entryName}DP;" + $flag_comment + "\n" - insert_target_line(target_file, target_line, "// end_of_coin_dipatcher_declarations_marker_do_not_modify\n") - target_line = " case TWBlockchain#{entryName}: entry = &#{entryName}DP; break;" + $flag_comment + "\n" - insert_target_line(target_file, target_line, " // end_of_coin_dipatcher_switch_marker_do_not_modify\n") -end - -def self.insert_target_line(target_file, target_line, original_line) - lines = File.readlines(target_file) - index = lines.index(target_line) - if !index.nil? - puts "Line is already present, file: #{target_file} line: #{target_line}" - return true - end - index = lines.index(original_line) - if index.nil? - puts "WARNING: Could not find line! file: #{target_file} line: #{original_line}" - return false - end - lines.insert(index, target_line) - File.open(target_file, "w+") do |f| - f.puts(lines) - end - puts "Updated file: #{target_file} new line: #{target_line}" - return true -end +require 'coin_skeleton_gen' command_line_args = ARGV if command_line_args.length < 1 @@ -99,49 +17,5 @@ if command_line_args.length < 1 end coin_id = command_line_args[0] -puts "New coin template for coin '#{coin_id}' requested" - -json_string = File.read('registry.json') -coins = JSON.parse(json_string).sort_by { |x| x['name'] } - -entity = EntityDecl.new(name: "New" + coin_id, is_struct: false, comment: '') -file = "new"+ coin_id - -generator = CodeGenerator.new(entities: [entity], files: [file], output_folder: ".") - -@coins = coins - -coin_test_gen = CoinTestGen.new() - -# Find coin in list of coins, by Id -coinSelect = coins.select {|c| c['id'] == coin_id} -if coinSelect.length() == 0 - puts "Error: coin #{coin_id} not found!" - return -end -coin = coinSelect.first -name = format_name(coin) - - -insert_coin_type(coin) -insert_coin_entry(coin) - -generate_file("newcoin/Address.h.erb", "src/#{name}", "Address.h", coin) -generate_file("newcoin/Address.cpp.erb", "src/#{name}", "Address.cpp", coin) -generate_file("newcoin/Entry.h.erb", "src/#{name}", "Entry.h", coin) -generate_file("newcoin/Entry.cpp.erb", "src/#{name}", "Entry.cpp", coin) -generate_file("newcoin/Proto.erb", "src/proto", "#{name}.proto", coin) -generate_file("newcoin/Signer.h.erb", "src/#{name}", "Signer.h", coin) -generate_file("newcoin/Signer.cpp.erb", "src/#{name}", "Signer.cpp", coin) - -generate_file("newcoin/AddressTests.cpp.erb", "tests/chains/#{name}", "AddressTests.cpp", coin) -generate_file("newcoin/SignerTests.cpp.erb", "tests/chains/#{name}", "SignerTests.cpp", coin) -generate_file("newcoin/TWAddressTests.cpp.erb", "tests/chains/#{name}", "TWAnyAddressTests.cpp", coin) -generate_file("newcoin/TWSignerTests.cpp.erb", "tests/chains/#{name}", "TWAnySignerTests.cpp", coin) -generate_file("newcoin/AddressTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Address.kt", coin) -generate_file("newcoin/SignerTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Signer.kt", coin) -generate_file("newcoin/Tests.swift.erb", "swift/Tests/Blockchains", "#{name}Tests.swift", coin) - -coin_test_gen.generate_coin_test_file(coin, 'TWCoinTypeTests.cpp.erb', true) -puts "please tools/generate-files to generate Swift/Java/Protobuf files" +generate_skeleton(coin_id, "") diff --git a/codegen/bin/newevmchain b/codegen/bin/newevmchain new file mode 100755 index 00000000000..e5fed2a97fb --- /dev/null +++ b/codegen/bin/newevmchain @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby + +# Sript for creating new skeleton files for a new EVM chain, subset of newcoin +# 1. Add relevant entry to registry.json (in order to minimize merge conflict, don't add at the very end) +# 2. Invoke this script with the id of the coin, e.g.: codegen/bin/newevmchain ethereumclone + +require 'fileutils' + +CurrentDir = File.dirname(__FILE__) +$LOAD_PATH.unshift(File.join(CurrentDir, '..', 'lib')) +require 'coin_skeleton_gen' + +command_line_args = ARGV +if command_line_args.length < 1 + puts "Usage: newevmchain " + return +end + +coin_id = command_line_args[0] + +generate_skeleton(coin_id, "evm") diff --git a/codegen/lib/coin_skeleton_gen.rb b/codegen/lib/coin_skeleton_gen.rb new file mode 100755 index 00000000000..c8adec9d8be --- /dev/null +++ b/codegen/lib/coin_skeleton_gen.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require 'erb' +require 'fileutils' +require 'json' + +require 'entity_decl' +require 'code_generator' +require 'coin_test_gen' + +# Coin template generation + +$flag_comment = " // TODO remove if the blockchain already exists, or just remove this comment if not" + +# Transforms a coin name to a C++ name +def self.format_name(coin) + formatted = coin['name'] + formatted = formatted.gsub(/-/, ' ') + formatted = formatted.gsub(/\./, ' ') + formatted = formatted.gsub(/\s/, '') + formatted +end + +def self.format_name_lowercase(coin) +format_name(coin).downcase +end + +def self.format_name_uppercase(coin) +format_name(coin).upcase +end + +def self.generate_file(templateFile, folder, fileName, coin) + @coin = coin + name = format_name(coin) + path = File.expand_path(templateFile, File.join(File.dirname(__FILE__), '..', 'lib', 'templates')) + template = ERB.new(File.read(path), nil, '-') + result = template.result(binding) + + FileUtils.mkdir_p folder + path = File.join(folder, fileName) + File.write(path, result) + puts "Generated file " + path +end + +def self.insert_coin_type(coin, mode) + target_file = "include/TrustWalletCore/TWCoinType.h" + target_line = " TWCoinType#{format_name(coin)} = #{coin['coinId']},\n" + if insert_target_line(target_file, target_line, "};\n") + if (mode != "evm") + insert_blockchain_type(coin) + end + end +end + +def insert_blockchain_type(coin) + target_file = "include/TrustWalletCore/TWBlockchain.h" + line_number = File.readlines(target_file).count + 2 # add offset because of removed blockchain enum type + target_line = " TWBlockchain#{coin['blockchain']} = #{line_number - 17}, " + $flag_comment + "\n" + insert_target_line(target_file, target_line, "};\n") +end + +def insert_coin_entry(coin) + target_file = "src/Coin.cpp" + entryName = coin['blockchain'] + target_line = "#include \"#{entryName}/Entry.h\"" + $flag_comment + "\n" + insert_target_line(target_file, target_line, "// end_of_coin_includes_marker_do_not_modify\n") + target_line = "#{entryName}::Entry #{entryName}DP;" + $flag_comment + "\n" + insert_target_line(target_file, target_line, "// end_of_coin_dipatcher_declarations_marker_do_not_modify\n") + target_line = " case TWBlockchain#{entryName}: entry = &#{entryName}DP; break;" + $flag_comment + "\n" + insert_target_line(target_file, target_line, " // end_of_coin_dipatcher_switch_marker_do_not_modify\n") +end + +def self.insert_target_line(target_file, target_line, original_line) + lines = File.readlines(target_file) + index = lines.index(target_line) + if !index.nil? + puts "Line is already present, file: #{target_file} line: #{target_line}" + return true + end + index = lines.index(original_line) + if index.nil? + puts "WARNING: Could not find line! file: #{target_file} line: #{original_line}" + return false + end + lines.insert(index, target_line) + File.open(target_file, "w+") do |f| + f.puts(lines) + end + puts "Updated file: #{target_file} new line: #{target_line}" + return true +end + +def generate_skeleton(coin_id, mode) + puts "New coin template for coin '#{coin_id}' #{mode} requested" + + json_string = File.read('registry.json') + coins = JSON.parse(json_string).sort_by { |x| x['name'] } + + entity = EntityDecl.new(name: "New" + coin_id, is_struct: false, comment: '') + file = "new"+ coin_id + + generator = CodeGenerator.new(entities: [entity], files: [file], output_folder: ".") + + @coins = coins + + coin_test_gen = CoinTestGen.new() + + # Find coin in list of coins, by Id + coinSelect = coins.select {|c| c['id'] == coin_id} + if coinSelect.length() == 0 + puts "Error: coin #{coin_id} not found!" + return + end + coin = coinSelect.first + name = format_name(coin) + + + insert_coin_type(coin, mode) + if (mode != "evm") + insert_coin_entry(coin) + + generate_file("newcoin/Address.h.erb", "src/#{name}", "Address.h", coin) + generate_file("newcoin/Address.cpp.erb", "src/#{name}", "Address.cpp", coin) + generate_file("newcoin/Entry.h.erb", "src/#{name}", "Entry.h", coin) + generate_file("newcoin/Entry.cpp.erb", "src/#{name}", "Entry.cpp", coin) + generate_file("newcoin/Proto.erb", "src/proto", "#{name}.proto", coin) + generate_file("newcoin/Signer.h.erb", "src/#{name}", "Signer.h", coin) + generate_file("newcoin/Signer.cpp.erb", "src/#{name}", "Signer.cpp", coin) + + generate_file("newcoin/AddressTests.cpp.erb", "tests/chains/#{name}", "AddressTests.cpp", coin) + generate_file("newcoin/SignerTests.cpp.erb", "tests/chains/#{name}", "SignerTests.cpp", coin) + generate_file("newcoin/TWAddressTests.cpp.erb", "tests/chains/#{name}", "TWAnyAddressTests.cpp", coin) + generate_file("newcoin/TWSignerTests.cpp.erb", "tests/chains/#{name}", "TWAnySignerTests.cpp", coin) + generate_file("newcoin/AddressTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Address.kt", coin) + generate_file("newcoin/SignerTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Signer.kt", coin) + generate_file("newcoin/Tests.swift.erb", "swift/Tests/Blockchains", "#{name}Tests.swift", coin) + end + + coin_test_gen.generate_coin_test_file(coin, 'TWCoinTypeTests.cpp.erb', true) + + puts "please tools/generate-files to generate Swift/Java/Protobuf files" +end From df1b2236fad8ed62fb5551b3d373af164880afce Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 16 Dec 2022 15:37:11 +0100 Subject: [PATCH 064/426] [ThorSwap]: full DOGE/BCH/LTC support (#2798) --- src/THORChain/Swap.cpp | 33 ++++- src/THORChain/Swap.h | 5 +- tests/chains/THORChain/SwapTests.cpp | 193 +++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 8 deletions(-) diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index 829f104ba69..e23c4ec999a 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -50,6 +50,12 @@ TWCoinType chainCoinType(Chain chain) { return TWCoinTypeBinance; case Chain::BTC: return TWCoinTypeBitcoin; + case Chain::DOGE: + return TWCoinTypeDogecoin; + case Chain::BCH: + return TWCoinTypeBitcoinCash; + case Chain::LTC: + return TWCoinTypeLitecoin; case Chain::THOR: default: return TWCoinTypeTHORChain; @@ -64,6 +70,12 @@ std::string chainName(Chain chain) { return "BNB"; case Chain::BTC: return "BTC"; + case Chain::DOGE: + return "DOGE"; + case Chain::BCH: + return "BCH"; + case Chain::LTC: + return "LTC"; case Chain::THOR: default: return "THOR"; @@ -89,8 +101,11 @@ SwapBundled SwapBuilder::build(bool shortened) { const auto memo = this->buildMemo(shortened); switch (fromChain) { - case Chain::BTC: { - return buildBitcoin(fromAmountNum, memo); + case Chain::BTC: + case Chain::DOGE: + case Chain::BCH: + case Chain::LTC: { + return buildBitcoin(fromAmountNum, memo, fromChain); case Chain::BNB: return buildBinance(mFromAsset, fromAmountNum, memo); case Chain::ETH: @@ -110,12 +125,15 @@ std::string SwapBuilder::buildMemo(bool shortened) noexcept { const auto& toSymbol = mToAsset.symbol(); const auto toCoinToken = (!toTokenId.empty() && toTokenId != "0x0000000000000000000000000000000000000000") ? toTokenId : toSymbol; std::stringstream memo; - memo << prefix + ":" + chainName(toChain) + "." + toCoinToken + ":" + mToAddress + ":" + std::to_string(toAmountLimitNum); + memo << prefix + ":" + chainName(toChain) + "." + toCoinToken + ":" + mToAddress; + if (toAmountLimitNum > 0) { + memo << ":" << std::to_string(toAmountLimitNum); + } if (mAffFeeAddress.has_value() || mAffFeeRate.has_value() || mExtraMemo.has_value()) { memo << ":"; if (mAffFeeAddress.has_value()) { - memo << mAffFeeAddress.value(); + memo << mAffFeeAddress.value(); } if (mAffFeeRate.has_value() || mExtraMemo.has_value()) { memo << ":"; @@ -131,11 +149,12 @@ std::string SwapBuilder::buildMemo(bool shortened) noexcept { return memo.str(); } -SwapBundled SwapBuilder::buildBitcoin(uint64_t amount, const std::string& memo) { +SwapBundled SwapBuilder::buildBitcoin(uint64_t amount, const std::string& memo, Chain fromChain) { auto input = Bitcoin::Proto::SigningInput(); Data out; // Following fields must be set afterwards, before signing ... - input.set_hash_type(TWBitcoinSigHashTypeAll); + auto coinType = chainCoinType(fromChain); + input.set_hash_type(Bitcoin::hashTypeForCoin(coinType)); input.set_byte_fee(1); input.set_use_max_amount(false); // private_key[] @@ -146,7 +165,7 @@ SwapBundled SwapBuilder::buildBitcoin(uint64_t amount, const std::string& memo) input.set_amount(static_cast(amount)); input.set_to_address(mVaultAddress); input.set_change_address(mFromAddress); - input.set_coin_type(TWCoinTypeBitcoin); + input.set_coin_type(coinType); input.set_output_op_return(memo); auto serialized = input.SerializeAsString(); diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h index bd4d253319b..504553567ef 100644 --- a/src/THORChain/Swap.h +++ b/src/THORChain/Swap.h @@ -21,6 +21,9 @@ enum Chain { BTC = 1, ETH = 2, BNB = 3, + DOGE = 4, + BCH = 5, + LTC = 6 }; using SwapErrorCode = int; @@ -44,7 +47,7 @@ class SwapBuilder { std::optional mAffFeeRate{std::nullopt}; std::optional mExtraMemo{std::nullopt}; - SwapBundled buildBitcoin(uint64_t amount, const std::string& memo); + SwapBundled buildBitcoin(uint64_t amount, const std::string& memo, Chain fromChain); SwapBundled buildBinance(Proto::Asset fromAsset, uint64_t amount, const std::string& memo); SwapBundled buildEth(uint64_t amount, const std::string& memo); diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 591ed6b9e07..54bb152bb3c 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -107,6 +107,199 @@ TEST(THORChainSwap, SwapBtcEth) { ); } +TEST(THORChainSwap, SwapDogeBusd) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::DOGE)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + toAsset.set_token_id("BUSD-BD1"); + + auto vaultDoge = "DExct9oTfqr7pfnbP2hkCHP1Z2eUDgqXya"; + auto fromAddressDoge = "DKftkYCtCyYxQy2TRAuAzQXoyKDdYsEBnw"; + auto toAddressBnb = "bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2"; + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(fromAddressDoge) + .toAddress(toAddressBnb) + .vault(vaultDoge) + .fromAmount("10000000000") + .toAmountLimit("789627468") + .affFeeAddress("t") + .affFeeRate("0") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "08011080c8afa025180122224445786374396f546671723770666e625032686b434850315a3265554467715879612a22444b66746b5943744379597851793254524175417a51586f794b4464597345426e7750036a473d3a424e422e425553442d4244313a626e623173346b616c6c786e67707973707a6d366e72657a6b6d6c3972677977366b78707734666872323a3738393632373436383a743a30"); + + auto tx = Bitcoin::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); + + // check fields + EXPECT_EQ(tx.amount(), 10000000000); + EXPECT_EQ(tx.to_address(), vaultDoge); + EXPECT_EQ(tx.change_address(), fromAddressDoge); + EXPECT_EQ(tx.output_op_return(), "=:BNB.BUSD-BD1:bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2:789627468:t:0"); + EXPECT_EQ(tx.coin_type(), TWCoinTypeDogecoin); + EXPECT_EQ(tx.private_key_size(), 0); + EXPECT_FALSE(tx.has_plan()); + + // + auto dogeKey = parse_hex("3785864c91ed408ebaeae473962a471eb4d68ce998c2957e8e5f6be7a525f2d7"); + tx.add_private_key(dogeKey.data(), dogeKey.size()); + tx.set_byte_fee(1000); + auto& utxo = *tx.add_utxo(); + Data previousUTXOHash = parse_hex("9989c36afdd1755a679226875425b368816031186c0f1b4a363ab2ef6d0a2fe8"); + std::reverse(previousUTXOHash.begin(), previousUTXOHash.end()); + utxo.mutable_out_point()->set_hash(previousUTXOHash.data(), previousUTXOHash.size()); + utxo.mutable_out_point()->set_index(1); + utxo.mutable_out_point()->set_sequence(UINT32_MAX - 3); + auto utxoScript = Bitcoin::Script::lockScriptForAddress(fromAddressDoge, TWCoinTypeDogecoin); + utxo.set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo.set_amount(16845776096); + tx.set_use_max_amount(false); + + // sign and encode resulting input + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeDogecoin); + EXPECT_EQ(output.error(), 0); + EXPECT_EQ(hex(output.encoded()), "0100000001e82f0a6defb23a364a1b0f6c1831608168b32554872692675a75d1fd6ac38999010000006b4830450221008660de3d3123a9e6831517265fb84c4fb2bfc4b98366dbfb4b63bc78a5812cce02201a0673af15edab604d9cd89f0e2842ccdd973e107ff9cd08dcf45d8c0b27c5dd0121039535d01e184b4a6d624e7ab007612e2558697fbed29274e6474f17e70d31ce5afcffffff0300e40b54020000001976a9146bb602e5e8eca75c7f6f25f766254658581db71688ac40490698010000001976a9149f64d0c07876a1dbce40cdce328bc7ecd8182b2288ac0000000000000000496a473d3a424e422e425553442d4244313a626e623173346b616c6c786e67707973707a6d366e72657a6b6d6c3972677977366b78707734666872323a3738393632373436383a743a3000000000"); + + // similar real transaction: + // https://viewblock.io/thorchain/tx/E7588A6A4C6B9DBA8B9AD8B0834655F9D9E5861744B5493E711623E320B981A5 + // https://dogechain.info/tx/e7588a6a4c6b9dba8b9ad8b0834655f9d9e5861744b5493e711623e320b981a5 + // https://binance.mintscan.io/txs/A5943D315BFD501DD5FC212F5A505772A20DDB154A8B5760A9897ABB8114CBDB +} + +TEST(THORChainSwap, SwapLtcBusd) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::LTC)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + toAsset.set_token_id("BUSD-BD1"); + + auto vaultLTC = "ltc1qmca5runvg3hygarulu34evdulcdfda7z7zquhn"; + auto fromAddressLTC = "ltc1qyu9qvkukx99r6yadxlk3t2x78a7dxe73s3r4x3"; + auto toAddressBnb = "bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2"; + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(fromAddressLTC) + .toAddress(toAddressBnb) + .vault(vaultLTC) + .fromAmount("15000000") + .toAmountLimit("977240514") + .affFeeAddress("t") + .affFeeRate("0") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "080110c0c393071801222b6c746331716d63613572756e7667336879676172756c753334657664756c6364666461377a377a7175686e2a2b6c7463317179753971766b756b7839397236796164786c6b3374327837386137647865373373337234783350026a473d3a424e422e425553442d4244313a626e623173346b616c6c786e67707973707a6d366e72657a6b6d6c3972677977366b78707734666872323a3937373234303531343a743a30"); + + auto tx = Bitcoin::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); + + // check fields + EXPECT_EQ(tx.amount(), 15000000); + EXPECT_EQ(tx.to_address(), vaultLTC); + EXPECT_EQ(tx.change_address(), fromAddressLTC); + EXPECT_EQ(tx.output_op_return(), "=:BNB.BUSD-BD1:bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2:977240514:t:0"); + EXPECT_EQ(tx.coin_type(), TWCoinTypeLitecoin); + EXPECT_EQ(tx.private_key_size(), 0); + EXPECT_FALSE(tx.has_plan()); + + // + auto ltcKey = parse_hex("6affb3d4e2c4f5a23b711e67ca94d0bd93550e203f5c8258df74cc62282d1494"); + tx.add_private_key(ltcKey.data(), ltcKey.size()); + tx.set_byte_fee(140); + auto& utxo = *tx.add_utxo(); + Data previousUTXOHash = parse_hex("6e71e6da1898584ccf92c362db3d7c16326f9daae6687132c69abfdb043cc749"); + std::reverse(previousUTXOHash.begin(), previousUTXOHash.end()); + utxo.mutable_out_point()->set_hash(previousUTXOHash.data(), previousUTXOHash.size()); + utxo.mutable_out_point()->set_index(0); + utxo.mutable_out_point()->set_sequence(UINT32_MAX - 3); + auto utxoScript = Bitcoin::Script::lockScriptForAddress(fromAddressLTC, TWCoinTypeLitecoin); + utxo.set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo.set_amount(34183600); + tx.set_use_max_amount(false); + + // sign and encode resulting input + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeLitecoin); + EXPECT_EQ(output.error(), 0); + EXPECT_EQ(hex(output.encoded()), "0100000000010149c73c04dbbf9ac6327168e6aa9d6f32167c3ddb62c392cf4c589818dae6716e0000000000fcffffff03c0e1e40000000000160014de3b41f26c446e44747cff235cb1bcfe1a96f7c2fc3d240100000000160014270a065b96314a3d13ad37ed15a8de3f7cd367d10000000000000000496a473d3a424e422e425553442d4244313a626e623173346b616c6c786e67707973707a6d366e72657a6b6d6c3972677977366b78707734666872323a3937373234303531343a743a3002483045022100fb9df5ef12c26648a50af298c5319ec52ea0287aa1405e07d817c606bb17a23502206520b087a9155a7d8c04b54b8ee3405fad9c3d22cf2c7cac06197ce555d56077012103acefb7d95b8c1da28f17400740d7e1124dbee3cfbe55646deb28198d570ea26b00000000"); + + // https://viewblock.io/thorchain/tx/FBB450335ED839C5FE3DCB9CBC0999DA6E6E52B787D1B165D3FA47E6273CCF5F + // https://blockchair.com/litecoin/transaction/fbb450335ed839c5fe3dcb9cbc0999da6e6e52b787d1b165d3fa47e6273ccf5f + // https://binance.mintscan.io/txs/7071DF040641D9C62EAA5D7AE5CDAC0C408FE64406261EC32417BD919684707C +} + +TEST(THORChainSwap, SwapBchBusd) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::BCH)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + toAsset.set_token_id("BUSD-BD1"); + + auto vaultBCH = "qpsfh5xvk7mgf9e6kl4e045nm6awl5hmks9x7h5ad6"; + auto fromAddressBCH = "qr50u7hy3xcr3j0w9j5nfx2gevjqgfm42ykc2hqgy4"; + auto toAddressBnb = "bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2"; + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(fromAddressBCH) + .toAddress(toAddressBnb) + .vault(vaultBCH) + .fromAmount("10000000") + .toAmountLimit("977240514") + .affFeeAddress("t") + .affFeeRate("0") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "08411080ade2041801222a71707366683578766b376d67663965366b6c34653034356e6d3661776c35686d6b7339783768356164362a2a717235307537687933786372336a3077396a356e6678326765766a7167666d3432796b633268716779345091016a473d3a424e422e425553442d4244313a626e623173346b616c6c786e67707973707a6d366e72657a6b6d6c3972677977366b78707734666872323a3937373234303531343a743a30"); + + auto tx = Bitcoin::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); + + // check fields + EXPECT_EQ(tx.amount(), 10000000); + EXPECT_EQ(tx.to_address(), vaultBCH); + EXPECT_EQ(tx.change_address(), fromAddressBCH); + EXPECT_EQ(tx.output_op_return(), "=:BNB.BUSD-BD1:bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2:977240514:t:0"); + EXPECT_EQ(tx.coin_type(), TWCoinTypeBitcoinCash); + EXPECT_EQ(tx.private_key_size(), 0); + EXPECT_FALSE(tx.has_plan()); + + // + auto bchKey = parse_hex("1a3b0105a08908734ed0525f4c6fadca068514cdeb732d7ebca5b0fcbe6952a7"); + tx.add_private_key(bchKey.data(), bchKey.size()); + tx.set_byte_fee(3); + auto& utxo = *tx.add_utxo(); + Data previousUTXOHash = parse_hex("651e5d3a60f8110a6cfb745005168bdfcaf21e7f2f4371873a24b5cd894564da"); + std::reverse(previousUTXOHash.begin(), previousUTXOHash.end()); + utxo.mutable_out_point()->set_hash(previousUTXOHash.data(), previousUTXOHash.size()); + utxo.mutable_out_point()->set_index(1); + utxo.mutable_out_point()->set_sequence(UINT32_MAX - 3); + auto utxoScript = Bitcoin::Script::lockScriptForAddress(fromAddressBCH, TWCoinTypeBitcoinCash); + utxo.set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo.set_amount(14118938); + tx.set_use_max_amount(false); + + // sign and encode resulting input + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBitcoinCash); + EXPECT_EQ(output.error(), 0); + EXPECT_EQ(hex(output.encoded()), "0100000001da644589cdb5243a8771432f7f1ef2cadf8b16055074fb6c0a11f8603a5d1e65010000006a4730440220392fab53b86e02bef19638077fd378dd713dd6b1968d07f4507e28feb022d52a02200240bb2f2e8b8eb7673c4bc69b485e28a0d56c735d84e3f794c303c1b71759e941210393dc5157b5879cd602f25529437e01b3d4892a4b9b8d9efcaa640d842b27438efcffffff0380969800000000001976a914609bd0ccb7b684973ab7eb97d693debaefd2fbb488ac8ed63e00000000001976a914e8fe7ae489b038c9ee2ca9349948cb240427755188ac0000000000000000496a473d3a424e422e425553442d4244313a626e623173346b616c6c786e67707973707a6d366e72657a6b6d6c3972677977366b78707734666872323a3937373234303531343a743a3000000000"); + + // https://viewblock.io/thorchain/tx/B8AA6F2BFD09D7AC510BFCDA417903B2DDBEE0E9811821640D9C304B9B382B9B + // https://blockchair.com/bitcoin-cash/transaction/b8aa6f2bfd09d7ac510bfcda417903b2ddbee0e9811821640d9c304b9b382b9b + // https://binance.mintscan.io/txs/F4CD6554934E85D72269399607A7ADF8A92378C2287C164CC97CB57E8348B090 +} + TEST(THORChainSwap, SwapBtcBnb) { Proto::Asset fromAsset; fromAsset.set_chain(static_cast(Chain::BTC)); From 0fd392a29721c8ddd2f6392b549c751c0e2aa6b8 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 21 Dec 2022 11:44:40 +0100 Subject: [PATCH 065/426] [Immutable X]: Complete implementation (#2729) --- .../ethereum/TestEthereumMessageSigner.kt | 27 + .../starkex/TestStarkExMessageSigner.kt | 27 + .../core/app/utils/TestHDWallet.kt | 41 +- .../core/app/utils/TestPrivateKey.kt | 18 + .../core/app/utils/TestPublicKey.kt | 16 +- include/TrustWalletCore/TWCurve.h | 3 +- include/TrustWalletCore/TWEthereumEip2645.h | 28 + .../TrustWalletCore/TWEthereumMessageSigner.h | 41 + include/TrustWalletCore/TWHDWallet.h | 1 + include/TrustWalletCore/TWPublicKeyType.h | 1 + .../TrustWalletCore/TWStarkExMessageSigner.h | 40 + include/TrustWalletCore/TWStarkWare.h | 27 + rust/Cargo.lock | 866 ++++++++++++++++++ rust/Cargo.toml | 3 + rust/src/lib.rs | 1 + rust/src/starknet/mod.rs | 53 ++ src/Ethereum/EIP191.cpp | 38 + src/Ethereum/EIP191.h | 31 + src/Ethereum/EIP2645.cpp | 37 + src/Ethereum/EIP2645.h | 15 + src/HDWallet.cpp | 206 +++-- src/HDWallet.h | 17 +- src/HexCoding.h | 51 +- src/ImmutableX/Constants.h | 24 + src/ImmutableX/StarkKey.cpp | 83 ++ src/ImmutableX/StarkKey.h | 32 + src/Keystore/StoredKey.cpp | 20 +- src/Keystore/StoredKey.h | 16 +- src/PrivateKey.cpp | 18 +- src/PublicKey.cpp | 6 + src/PublicKey.h | 3 + src/StarkEx/MessageSigner.cpp | 23 + src/StarkEx/MessageSigner.h | 28 + src/XRP/Transaction.cpp | 5 +- src/interface/TWEthereumEip2645.cpp | 18 + src/interface/TWEthereumMessageSigner.cpp | 22 + src/interface/TWHDWallet.cpp | 4 +- src/interface/TWStarkExMessageSigner.cpp | 22 + src/interface/TWStarkWare.cpp | 17 + swift/Tests/Blockchains/EthereumTests.swift | 11 +- swift/Tests/Blockchains/StarkExTests.swift | 19 + swift/Tests/HDWalletTests.swift | 32 + swift/Tests/PrivateKeyTests.swift | 16 + swift/Tests/PublicKeyTests.swift | 10 + tests/chains/Ethereum/EIP191Tests.cpp | 37 + tests/chains/Ethereum/SignerTests.cpp | 4 +- tests/chains/ImmutableX/StarkKeyTests.cpp | 108 +++ tests/chains/StarkEx/MessageSignerTests.cpp | 37 + .../common/HDWallet/HDWalletInternalTests.cpp | 2 +- tests/common/HDWallet/HDWalletTests.cpp | 192 +++- tests/interface/TWHDWalletTests.cpp | 45 + walletconsole/lib/Address.cpp | 6 +- 52 files changed, 2289 insertions(+), 159 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/starkex/TestStarkExMessageSigner.kt create mode 100644 include/TrustWalletCore/TWEthereumEip2645.h create mode 100644 include/TrustWalletCore/TWEthereumMessageSigner.h create mode 100644 include/TrustWalletCore/TWStarkExMessageSigner.h create mode 100644 include/TrustWalletCore/TWStarkWare.h create mode 100644 rust/src/starknet/mod.rs create mode 100644 src/Ethereum/EIP191.cpp create mode 100644 src/Ethereum/EIP191.h create mode 100644 src/Ethereum/EIP2645.cpp create mode 100644 src/Ethereum/EIP2645.h create mode 100644 src/ImmutableX/Constants.h create mode 100644 src/ImmutableX/StarkKey.cpp create mode 100644 src/ImmutableX/StarkKey.h create mode 100644 src/StarkEx/MessageSigner.cpp create mode 100644 src/StarkEx/MessageSigner.h create mode 100644 src/interface/TWEthereumEip2645.cpp create mode 100644 src/interface/TWEthereumMessageSigner.cpp create mode 100644 src/interface/TWStarkExMessageSigner.cpp create mode 100644 src/interface/TWStarkWare.cpp create mode 100644 swift/Tests/Blockchains/StarkExTests.swift create mode 100644 tests/chains/Ethereum/EIP191Tests.cpp create mode 100644 tests/chains/ImmutableX/StarkKeyTests.cpp create mode 100644 tests/chains/StarkEx/MessageSignerTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt new file mode 100644 index 00000000000..0f0aae9efc4 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt @@ -0,0 +1,27 @@ +package com.trustwallet.core.app.blockchains.ethereum + +import com.trustwallet.core.app.utils.Numeric +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import wallet.core.jni.CoinType +import wallet.core.jni.EthereumMessageSigner +import wallet.core.jni.PrivateKey + +class TestEthereumMessageSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testEthereumSignAndVerifyMessage() { + val data = Numeric.hexStringToByteArray("3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84") + val privateKey = PrivateKey(data) + val publicKey = privateKey.getPublicKey(CoinType.ETHEREUM) + val msg = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3" + val signature = EthereumMessageSigner.signMessage(privateKey, msg) + assertEquals(signature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); + assertTrue(EthereumMessageSigner.verifyMessage(publicKey, msg, signature)) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/starkex/TestStarkExMessageSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/starkex/TestStarkExMessageSigner.kt new file mode 100644 index 00000000000..d68cd722b80 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/starkex/TestStarkExMessageSigner.kt @@ -0,0 +1,27 @@ +package com.trustwallet.core.app.blockchains.starkex + +import com.trustwallet.core.app.utils.Numeric +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import wallet.core.jni.PrivateKey +import wallet.core.jni.PublicKeyType +import wallet.core.jni.StarkExMessageSigner + +class TestStarkExMessageSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testStarkExSignAndVerifyMessage() { + val data = Numeric.hexStringToByteArray("04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de") + val privateKey = PrivateKey(data) + val publicKey = privateKey.getPublicKeyByType(PublicKeyType.STARKEX) + val msg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf" + val signature = StarkExMessageSigner.signMessage(privateKey, msg) + assertEquals(signature, "04cf5f21333dd189ada3c0f2a51430d733501a9b1d5e07905273c1938cfb261e05b6013d74adde403e8953743a338c8d414bb96bf69d2ca1a91a85ed2700a528"); + assertTrue(StarkExMessageSigner.verifyMessage(publicKey, msg, signature)) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt index ed1c171aa6b..07c91d0829b 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt @@ -8,19 +8,13 @@ package com.trustwallet.core.app.utils import com.trustwallet.core.app.utils.Numeric import com.trustwallet.core.app.utils.toHex -import wallet.core.jni.CoinType -import wallet.core.jni.Curve -import wallet.core.jni.Derivation -import wallet.core.jni.HDVersion -import wallet.core.jni.HDWallet -import wallet.core.jni.Mnemonic -import wallet.core.jni.Purpose import java.security.InvalidParameterException import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.Test +import wallet.core.jni.* class TestHDWallet { init { @@ -31,6 +25,39 @@ class TestHDWallet { "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal" val password = "TREZOR" + @Test + fun testCreateFromMnemonicImmutableXMainnetFromSignature() { + // Successfully register: https://api.x.immutable.com/v1/users/0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37 + val hd = HDWallet("obscure opera favorite shuffle mail tip age debate dirt pact cement loyal", "") + val derivationPath = EthereumEip2645.getPath("0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37", "starkex", "immutablex", "1") + assertEquals(derivationPath, "m/2645'/579218131'/211006541'/2124474935'/1609799702'/1") + + // Retrieve eth private key + val ethPrivateKey = hd.getKeyForCoin(CoinType.ETHEREUM) + assertEquals(Numeric.toHexString(ethPrivateKey.data()), "0x03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d") + + // Retrieve StarkKey DerivationPath + val starkDerivationPath = DerivationPath(derivationPath) + + // Retrieve Stark Private key part + val ethMsg = "Only sign this request if you’ve initiated an action with Immutable X." + val ethSignature = EthereumMessageSigner.signMessage(ethPrivateKey, ethMsg) + assertEquals(ethSignature, "18b1be8b78807d3326e28bc286d7ee3d068dcd90b1949ce1d25c1f99825f26e70992c5eb7f44f76b202aceded00d74f771ed751f2fe538eec01e338164914fe001") + val starkPrivateKey = StarkWare.getStarkKeyFromSignature(starkDerivationPath, ethSignature) + val starkPublicKey = starkPrivateKey.getPublicKeyByType(PublicKeyType.STARKEX) + assertEquals(Numeric.toHexString(starkPrivateKey.data()), "0x04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de") + assertEquals(Numeric.toHexString(starkPublicKey.data()), "0x00e5b9b11f8372610ef35d647a1dcaba1a4010716588d591189b27bf3c2d5095") + + // Account register + val ethMsgToRegister = "Only sign this key linking request from Immutable X" + val ethSignatureToRegister = EthereumMessageSigner.signMessage(ethPrivateKey, ethMsgToRegister) + assertEquals(ethSignatureToRegister, "646da4160f7fc9205e6f502fb7691a0bf63ecbb74bbb653465cd62388dd9f56325ab1e4a9aba99b1661e3e6251b42822855a71e60017b310b9f90e990a12e1dc01") + val starkMsg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf" + val starkSignature = StarkExMessageSigner.signMessage(starkPrivateKey, starkMsg) + assertEquals(starkSignature, "04cf5f21333dd189ada3c0f2a51430d733501a9b1d5e07905273c1938cfb261e05b6013d74adde403e8953743a338c8d414bb96bf69d2ca1a91a85ed2700a528") + assertTrue(StarkExMessageSigner.verifyMessage(starkPublicKey, starkMsg, starkSignature)) + } + @Test fun testCreateFromMnemonic() { val hd = HDWallet(words, password) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt index 4b6b4908db8..aeb9239e671 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt @@ -19,6 +19,24 @@ class TestPrivateKey { assertTrue(data.size == 32); } + @Test + fun testCreateStarkKey() { + val data = Numeric.hexStringToByteArray("06cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c") + assertTrue(PrivateKey.isValid(data, Curve.STARKEX)) + var privateKey = PrivateKey(data) + var pubKey = privateKey.getPublicKeyByType(PublicKeyType.STARKEX) + assertEquals(pubKey.data().toHex(), "0x02d2bbdc1adaf887b0027cdde2113cfd81c60493aa6dc15d7887ddf1a82bc831") + } + + @Test + fun testCreateStarkKeySigning() { + val data = Numeric.hexStringToByteArray("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79") + var privateKey = PrivateKey(data) + val digest = Numeric.hexStringToByteArray("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76") + val signature = privateKey.sign(digest, Curve.STARKEX) + assertEquals(signature.toHex(), "0x061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a") + } + @Test fun testInvalid() { val bytes = Numeric.hexStringToByteArray("deadbeaf") diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPublicKey.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPublicKey.kt index dbd57dc1425..bfecc51014e 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPublicKey.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPublicKey.kt @@ -4,10 +4,7 @@ import com.trustwallet.core.app.utils.toHexBytes import com.trustwallet.core.app.utils.toHex import org.junit.Assert.* import org.junit.Test -import wallet.core.jni.Curve -import wallet.core.jni.Hash -import wallet.core.jni.PrivateKey -import wallet.core.jni.PublicKey +import wallet.core.jni.* class TestPublicKey { @@ -28,6 +25,17 @@ class TestPublicKey { val publicKey = privateKey?.getPublicKeySecp256k1(true) assertEquals("0x0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1", publicKey?.data()?.toHex()) } + + @Test + fun testVerifyStarkey() { + val data = Numeric.hexStringToByteArray("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159") + val publicKey = PublicKey(data, PublicKeyType.STARKEX) + var signature = Numeric.hexStringToByteArray("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a") + val hash = Numeric.hexStringToByteArray("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76") + assertTrue(publicKey.verify(signature, hash)) + signature = Numeric.hexStringToByteArray("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9b") + assertFalse(publicKey.verify(signature, hash)) + } @Test fun testSign() { diff --git a/include/TrustWalletCore/TWCurve.h b/include/TrustWalletCore/TWCurve.h index 79d2891aa40..36f0edbd7f6 100644 --- a/include/TrustWalletCore/TWCurve.h +++ b/include/TrustWalletCore/TWCurve.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -19,6 +19,7 @@ enum TWCurve { TWCurveCurve25519 /* "curve25519" */, TWCurveNIST256p1 /* "nist256p1" */, TWCurveED25519ExtendedCardano /* "ed25519-cardano-seed" */, + TWCurveStarkex /* "starkex" */, TWCurveNone }; diff --git a/include/TrustWalletCore/TWEthereumEip2645.h b/include/TrustWalletCore/TWEthereumEip2645.h new file mode 100644 index 00000000000..f6315908c33 --- /dev/null +++ b/include/TrustWalletCore/TWEthereumEip2645.h @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 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 "TWString.h" + +TW_EXTERN_C_BEGIN + +TW_EXPORT_CLASS +struct TWEthereumEip2645; + +/// Generate a layer 2 eip2645 derivation path from eth address, layer, application and given index. +/// +/// \param wallet non-null TWHDWallet +/// \param ethAddress non-null Ethereum address +/// \param layer non-null layer 2 name (E.G starkex) +/// \param application non-null layer 2 application (E.G immutablex) +/// \param index non-null layer 2 index (E.G 1) +/// \return a valid eip2645 layer 2 derivation path as a string +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWEthereumEip2645GetPath(TWString* _Nonnull ethAddress, TWString* _Nonnull layer, TWString* _Nonnull application, TWString* _Nonnull index); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWEthereumMessageSigner.h b/include/TrustWalletCore/TWEthereumMessageSigner.h new file mode 100644 index 00000000000..1fb230b143f --- /dev/null +++ b/include/TrustWalletCore/TWEthereumMessageSigner.h @@ -0,0 +1,41 @@ +// Copyright © 2017-2022 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" +#include "TWPrivateKey.h" +#include "TWPublicKey.h" + +TW_EXTERN_C_BEGIN + +/// Ethereum message signing and verification. +/// +/// Ethereum and some other wallets support a message signing & verification format, to create a proof (a signature) +/// that someone has access to the private keys of a specific address. +TW_EXPORT_CLASS +struct TWEthereumMessageSigner; + +/// Sign a message. +/// +/// \param privateKey: the private key used for signing +/// \param message: A custom message which is input to the signing. +/// \returns the signature, Hex-encoded. On invalid input empty string is returned. Returned object needs to be deleted after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWEthereumMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message); + +/// Verify signature for a message. +/// +/// \param pubKey: pubKey that will verify and recover the message from the signature +/// \param message: the message signed (without prefix) +/// \param signature: in Hex-encoded form. +/// \returns false on any invalid input (does not throw), true if the message can be recovered from the signature +TW_EXPORT_STATIC_METHOD +bool TWEthereumMessageSignerVerifyMessage(const struct TWPublicKey* _Nonnull pubKey, TWString* _Nonnull message, TWString* _Nonnull signature); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWHDWallet.h b/include/TrustWalletCore/TWHDWallet.h index a6261f57a7b..84b951ddc4f 100644 --- a/include/TrustWalletCore/TWHDWallet.h +++ b/include/TrustWalletCore/TWHDWallet.h @@ -11,6 +11,7 @@ #include "TWCurve.h" #include "TWData.h" #include "TWDerivation.h" +#include "TWDerivationPath.h" #include "TWHDVersion.h" #include "TWPrivateKey.h" #include "TWPublicKey.h" diff --git a/include/TrustWalletCore/TWPublicKeyType.h b/include/TrustWalletCore/TWPublicKeyType.h index e9d0e53b347..c5ee21c5f74 100644 --- a/include/TrustWalletCore/TWPublicKeyType.h +++ b/include/TrustWalletCore/TWPublicKeyType.h @@ -21,6 +21,7 @@ enum TWPublicKeyType { TWPublicKeyTypeED25519Blake2b = 5, TWPublicKeyTypeCURVE25519 = 6, TWPublicKeyTypeED25519Cardano = 7, + TWPublicKeyTypeStarkex = 8, }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWStarkExMessageSigner.h b/include/TrustWalletCore/TWStarkExMessageSigner.h new file mode 100644 index 00000000000..a7569171c39 --- /dev/null +++ b/include/TrustWalletCore/TWStarkExMessageSigner.h @@ -0,0 +1,40 @@ +// Copyright © 2017-2022 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" +#include "TWPrivateKey.h" + +TW_EXTERN_C_BEGIN + +/// StarkEx message signing and verification. +/// +/// StarkEx and some other wallets support a message signing & verification format, to create a proof (a signature) +/// that someone has access to the private keys of a specific address. +TW_EXPORT_CLASS +struct TWStarkExMessageSigner; + +/// Sign a message. +/// +/// \param privateKey: the private key used for signing +/// \param message: A custom hex message which is input to the signing. +/// \returns the signature, Hex-encoded. On invalid input empty string is returned. Returned object needs to be deleted after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWStarkExMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message); + +/// Verify signature for a message. +/// +/// \param pubKey: pubKey that will verify and recover the message from the signature +/// \param message: the message signed (without prefix) in hex +/// \param signature: in Hex-encoded form. +/// \returns false on any invalid input (does not throw), true if the message can be recovered from the signature +TW_EXPORT_STATIC_METHOD +bool TWStarkExMessageSignerVerifyMessage(const struct TWPublicKey* _Nonnull pubKey, TWString* _Nonnull message, TWString* _Nonnull signature); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWStarkWare.h b/include/TrustWalletCore/TWStarkWare.h new file mode 100644 index 00000000000..0990d67be86 --- /dev/null +++ b/include/TrustWalletCore/TWStarkWare.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2022 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 "TWPrivateKey.h" +#include "TWString.h" +#include "TWDerivationPath.h" + +TW_EXTERN_C_BEGIN + +TW_EXPORT_CLASS +struct TWStarkWare; + +/// Generates the private stark key at the given derivation path from a valid eth signature +/// +/// \param derivationPath non-null StarkEx Derivation path +/// \param signature valid eth signature +/// \return The private key for the specified derivation path/signature +TW_EXPORT_STATIC_METHOD +struct TWPrivateKey* _Nonnull TWStarkWareGetStarkKeyFromSignature(const struct TWDerivationPath* _Nonnull derivationPath, TWString* _Nonnull signature); + +TW_EXTERN_C_END diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 18607412aba..00747873f1b 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -8,6 +8,95 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std", + "digest 0.9.0", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-trait" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bcs" version = "0.1.4" @@ -18,12 +107,239 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", +] + +[[package]] +name = "ethbloom" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-rlp", + "impl-serde", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -31,8 +347,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -41,12 +359,99 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + [[package]] name = "libc" version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + [[package]] name = "move-core-types" version = "0.0.4" @@ -62,18 +467,120 @@ dependencies = [ "serde_bytes", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + +[[package]] +name = "pest" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +dependencies = [ + "thiserror", + "ucd-trie", +] + [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + [[package]] name = "proc-macro2" version = "1.0.47" @@ -92,6 +599,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + [[package]] name = "rand" version = "0.8.5" @@ -142,6 +655,66 @@ dependencies = [ "syn", ] +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.147" @@ -171,6 +744,143 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "starknet-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95540acef038bfdf3c91da323cedf0fd335f73899152cabdf407033fc7560713" +dependencies = [ + "base64", + "ethereum-types", + "hex", + "serde", + "serde_json", + "serde_with", + "sha3", + "starknet-crypto", + "starknet-ff", + "thiserror", +] + +[[package]] +name = "starknet-crypto" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d5ba05430fcd7c107ad9e4433bb3958722b13d97a44fd07a18dcf0eb6da667" +dependencies = [ + "crypto-bigint", + "hex", + "hmac", + "num-bigint", + "num-integer", + "num-traits", + "rfc6979", + "sha2", + "starknet-ff", + "thiserror", + "zeroize", +] + +[[package]] +name = "starknet-ff" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a80865f37a0dcd198f427356d939f1495144c26ed5123b1418ef33a63f82655" +dependencies = [ + "ark-ff", + "crypto-bigint", + "getrandom", + "hex", + "serde", + "thiserror", +] + +[[package]] +name = "starknet-signers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8199e756cc1ba3a8cd8287368b2bbd3bf7a876e98b3eea8abc5e851ee58bc5" +dependencies = [ + "async-trait", + "starknet-core", + "starknet-crypto", + "thiserror", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.103" @@ -182,6 +892,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "thiserror" version = "1.0.37" @@ -202,12 +930,66 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wallet-core-rs" version = "0.1.0" @@ -215,6 +997,9 @@ dependencies = [ "bcs", "hex", "move-core-types", + "starknet-crypto", + "starknet-ff", + "starknet-signers", ] [[package]] @@ -222,3 +1007,84 @@ name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 9a75f71be9c..b3a7546634b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -11,6 +11,9 @@ crate-type = ["staticlib"] # Creates static lib [dependencies] move-core-types = { git = "https://github.com/move-language/move", rev = "f7137eabc2046f76fdad3ded2c51e03a3b1fbd01", features = ["address32"] } +starknet-crypto = "0.1.0" +starknet-ff = "0.1.0" +starknet-signers = "0.1.0" bcs = "0.1.4" hex = "0.4.3" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4f3c0712189..01bae1bc0ae 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -6,3 +6,4 @@ pub mod move_parser; pub mod memory; +pub mod starknet; diff --git a/rust/src/starknet/mod.rs b/rust/src/starknet/mod.rs new file mode 100644 index 00000000000..9da51860024 --- /dev/null +++ b/rust/src/starknet/mod.rs @@ -0,0 +1,53 @@ +// Copyright © 2017-2022 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. + +use std::ffi::{c_char, CStr}; +use starknet_crypto::{FieldElement, get_public_key, Signature}; +use starknet_signers::{SigningKey, VerifyingKey}; +use crate::memory; + +pub fn field_element_from_be_hex(hex: &str) -> FieldElement { + let decoded = hex::decode(hex.trim_start_matches("0x")).unwrap(); + + if decoded.len() > 32 { + panic!("hex string too long"); + } + + let mut buffer = [0u8; 32]; + buffer[(32 - decoded.len())..].copy_from_slice(&decoded[..]); + + FieldElement::from_bytes_be(&buffer).unwrap() +} + +#[no_mangle] +pub extern fn starknet_pubkey_from_private(priv_key: *const c_char) -> *const c_char { + let s = unsafe { CStr::from_ptr(priv_key).to_str().unwrap() }; + let private_key = field_element_from_be_hex(s); + let hx = format!("{:#02x}", get_public_key(&private_key)); + memory::c_string_standalone(hx) +} + +#[no_mangle] +pub extern fn starknet_sign(priv_key: *const c_char, hash: *const c_char) -> *const c_char { + let s = unsafe { CStr::from_ptr(priv_key).to_str().unwrap() }; + let private_key = field_element_from_be_hex(s); + let h = unsafe { CStr::from_ptr(hash).to_str().unwrap() }; + let hash_field = field_element_from_be_hex(h); + let signing_key = SigningKey::from_secret_scalar(private_key); + let signature = signing_key.sign(&hash_field).unwrap(); + let hx = format!("{}", signature); + memory::c_string_standalone(hx) +} + +#[no_mangle] +pub extern fn starknet_verify(pub_key: *const c_char, hash: *const c_char, r: *const c_char, s: *const c_char) -> bool { + let pub_key = unsafe { field_element_from_be_hex(CStr::from_ptr(pub_key).to_str().unwrap()) }; + let hash = unsafe { field_element_from_be_hex(CStr::from_ptr(hash).to_str().unwrap()) }; + let r = unsafe { field_element_from_be_hex(CStr::from_ptr(r).to_str().unwrap()) }; + let s = unsafe { field_element_from_be_hex(CStr::from_ptr(s).to_str().unwrap()) }; + let verifying_key = VerifyingKey::from_scalar(pub_key); + verifying_key.verify(&hash, &Signature { r, s }).unwrap_or(false) +} diff --git a/src/Ethereum/EIP191.cpp b/src/Ethereum/EIP191.cpp new file mode 100644 index 00000000000..309c61c959c --- /dev/null +++ b/src/Ethereum/EIP191.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 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 "EIP191.h" +#include "HexCoding.h" +#include +#include + +namespace TW::Ethereum::internal { + +Data generateMessage(const std::string& message) { + std::string prefix(1, MessageSigner::EthereumPrefix); + std::stringstream ss; + ss << prefix << MessageSigner::MessagePrefix << std::to_string(message.size()) << message; + Data signableMessage = Hash::keccak256(data(ss.str())); + return signableMessage; +} + +} // namespace TW::Ethereum::internal + +namespace TW::Ethereum { + +std::string MessageSigner::signMessage(const PrivateKey& privateKey, const std::string& message) { + auto signableMessage = internal::generateMessage(message); + return hex(privateKey.sign(signableMessage, TWCurveSECP256k1)); +} + +bool MessageSigner::verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept { + auto msg = internal::generateMessage(message); + auto rawSignature = parse_hex(signature); + auto recovered = publicKey.recover(rawSignature, msg); + return recovered == publicKey && publicKey.verify(rawSignature, msg); +} + +} // namespace TW::Ethereum diff --git a/src/Ethereum/EIP191.h b/src/Ethereum/EIP191.h new file mode 100644 index 00000000000..c1267ab0452 --- /dev/null +++ b/src/Ethereum/EIP191.h @@ -0,0 +1,31 @@ +// Copyright © 2017-2022 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 +#include + +namespace TW::Ethereum { + +class MessageSigner { +public: + /// Sign a message following EIP-191 + /// \param privateKey the private key to sign with + /// \param message message to sign + /// \return hex signed message + static std::string signMessage(const PrivateKey& privateKey, const std::string& message); + /// Verify a message following EIP-191 + /// \param publicKey publickey to verify the signed message + /// \param message message to be verified as a string + /// \param signature signature to verify the message against + /// \return true if the message match the signature, false otherwise + static bool verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept; + static constexpr auto MessagePrefix = "Ethereum Signed Message:\n"; + static constexpr std::uint8_t EthereumPrefix{0x19}; +}; + +} // namespace TW::Ethereum diff --git a/src/Ethereum/EIP2645.cpp b/src/Ethereum/EIP2645.cpp new file mode 100644 index 00000000000..9a601643999 --- /dev/null +++ b/src/Ethereum/EIP2645.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 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 +#include +#include + +#include + +namespace TW::Ethereum::internal { + +static std::string getIntFromBits(const std::string& hexString, std::size_t from, std::optional length = std::nullopt) { + const auto data = hex_str_to_bin_str(hexString); + const auto sub = data.substr(data.size() - from, length.value_or(std::string::npos)); + return std::to_string(std::stoll(sub, nullptr, 2)); +} + +} // namespace TW::Ethereum::internal + +namespace TW::Ethereum { + +// https://docs.starkware.co/starkex/key-derivation.html +std::string accountPathFromAddress(const std::string& ethAddress, const std::string& layer, const std::string& application, const std::string& index) noexcept { + using namespace internal; + std::stringstream out; + const auto layerHash = getIntFromBits(hex(Hash::sha256(data(layer))), 31); + const auto applicationHash = getIntFromBits(hex(Hash::sha256(data(application))), 31); + const auto ethAddress1 = getIntFromBits(ethAddress.substr(2), 31); + const auto ethAddress2 = getIntFromBits(ethAddress.substr(2), 62, 31); + out << "m/2645'/" << layerHash << "'/" << applicationHash << "'/" << ethAddress1 << "'/" << ethAddress2 << "'/" << index; + return out.str(); +} + +} // namespace TW::Ethereum diff --git a/src/Ethereum/EIP2645.h b/src/Ethereum/EIP2645.h new file mode 100644 index 00000000000..3dd582a4381 --- /dev/null +++ b/src/Ethereum/EIP2645.h @@ -0,0 +1,15 @@ +// Copyright © 2017-2022 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 + +namespace TW::Ethereum { + +std::string accountPathFromAddress(const std::string& ethAddress, const std::string& layer, const std::string& application, const std::string& index) noexcept;; + +} // namespace TW::Ethereum diff --git a/src/HDWallet.cpp b/src/HDWallet.cpp index db73dc18f9b..682bfcddc62 100644 --- a/src/HDWallet.cpp +++ b/src/HDWallet.cpp @@ -11,6 +11,7 @@ #include "Bitcoin/CashAddress.h" #include "Bitcoin/SegwitAddress.h" #include "Coin.h" +#include "ImmutableX/StarkKey.h" #include "Mnemonic.h" #include "memory/memzero_wrapper.h" @@ -34,15 +35,34 @@ namespace { uint32_t fingerprint(HDNode* node, Hash::Hasher hasher); std::string serialize(const HDNode* node, uint32_t fingerprint, uint32_t version, bool use_public, Hash::Hasher hasher); bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher, HDNode* node); -HDNode getNode(const HDWallet& wallet, TWCurve curve, const DerivationPath& derivationPath); -HDNode getMasterNode(const HDWallet& wallet, TWCurve curve); - const char* curveName(TWCurve curve); } // namespace const int MnemonicBufLength = Mnemonic::MaxWords * (BIP39_MAX_WORD_LENGTH + 3) + 20; // some extra slack -HDWallet::HDWallet(int strength, const std::string& passphrase) +template +HDWallet::HDWallet(const Data& seed) { + std::copy_n(seed.begin(), seedSize, this->seed.begin()); +} + +template +void HDWallet::updateSeedAndEntropy(bool check) { + assert(!check || Mnemonic::isValid(mnemonic)); // precondition + + // generate seed from mnemonic + mnemonic_to_seed(mnemonic.c_str(), passphrase.c_str(), seed.data(), nullptr); + + // generate entropy bits from mnemonic + Data entropyRaw((Mnemonic::MaxWords * Mnemonic::BitsPerWord) / 8); + // entropy is truncated to fully bytes, 4 bytes for each 3 words (=33 bits) + auto entropyBytes = mnemonic_to_bits(mnemonic.c_str(), entropyRaw.data()) / 33 * 4; + // copy to truncate + entropy = data(entropyRaw.data(), entropyBytes); + assert(!check || entropy.size() > 10); +} + +template +HDWallet::HDWallet(int strength, const std::string& passphrase) : passphrase(passphrase) { char buf[MnemonicBufLength]; const char* mnemonic_chars = mnemonic_generate(strength, buf, MnemonicBufLength); @@ -54,7 +74,8 @@ HDWallet::HDWallet(int strength, const std::string& passphrase) updateSeedAndEntropy(); } -HDWallet::HDWallet(const std::string& mnemonic, const std::string& passphrase, const bool check) +template +HDWallet::HDWallet(const std::string& mnemonic, const std::string& passphrase, const bool check) : mnemonic(mnemonic), passphrase(passphrase) { if (mnemonic.length() == 0 || (check && !Mnemonic::isValid(mnemonic))) { @@ -63,7 +84,8 @@ HDWallet::HDWallet(const std::string& mnemonic, const std::string& passphrase, c updateSeedAndEntropy(check); } -HDWallet::HDWallet(const Data& entropy, const std::string& passphrase) +template +HDWallet::HDWallet(const Data& entropy, const std::string& passphrase) : passphrase(passphrase) { char buf[MnemonicBufLength]; const char* mnemonic_chars = mnemonic_from_data(entropy.data(), static_cast(entropy.size()), buf, MnemonicBufLength); @@ -75,59 +97,80 @@ HDWallet::HDWallet(const Data& entropy, const std::string& passphrase) updateSeedAndEntropy(); } -HDWallet::~HDWallet() { +template +HDWallet::~HDWallet() { std::fill(seed.begin(), seed.end(), 0); std::fill(mnemonic.begin(), mnemonic.end(), 0); std::fill(passphrase.begin(), passphrase.end(), 0); } -void HDWallet::updateSeedAndEntropy(bool check) { - assert(!check || Mnemonic::isValid(mnemonic)); // precondition - - // generate seed from mnemonic - mnemonic_to_seed(mnemonic.c_str(), passphrase.c_str(), seed.data(), nullptr); +template +static HDNode getMasterNode(const HDWallet& wallet, TWCurve curve) { + const auto privateKeyType = PrivateKey::getType(curve); + HDNode node; + switch (privateKeyType) { + case TWPrivateKeyTypeCardano: { + // Derives the root Cardano HDNode from a passphrase and the entropy encoded in + // a BIP-0039 mnemonic using the Icarus derivation (V2) scheme + const auto entropy = wallet.getEntropy(); + uint8_t secret[CARDANO_SECRET_LENGTH]; + secret_from_entropy_cardano_icarus((const uint8_t*)"", 0, entropy.data(), int(entropy.size()), secret, nullptr); + hdnode_from_secret_cardano(secret, &node); + TW::memzero(secret, CARDANO_SECRET_LENGTH); + break; + } + case TWPrivateKeyTypeDefault: + default: + hdnode_from_seed(wallet.getSeed().data(), HDWallet::mSeedSize, curveName(curve), &node); + break; + } + return node; +} - // generate entropy bits from mnemonic - Data entropyRaw((Mnemonic::MaxWords * Mnemonic::BitsPerWord) / 8); - // entropy is truncated to fully bytes, 4 bytes for each 3 words (=33 bits) - auto entropyBytes = mnemonic_to_bits(mnemonic.c_str(), entropyRaw.data()) / 33 * 4; - // copy to truncate - entropy = data(entropyRaw.data(), entropyBytes); - assert(!check || entropy.size() > 10); +template +static HDNode getNode(const HDWallet& wallet, TWCurve curve, const DerivationPath& derivationPath) { + const auto privateKeyType = PrivateKey::getType(curve); + auto node = getMasterNode(wallet, curve); + for (auto& index : derivationPath.indices) { + switch (privateKeyType) { + case TWPrivateKeyTypeCardano: + hdnode_private_ckd_cardano(&node, index.derivationIndex()); + break; + case TWPrivateKeyTypeDefault: + default: + hdnode_private_ckd(&node, index.derivationIndex()); + break; + } + } + return node; } -PrivateKey HDWallet::getMasterKey(TWCurve curve) const { +template +PrivateKey HDWallet::getMasterKey(TWCurve curve) const { auto node = getMasterNode(*this, curve); auto data = Data(node.private_key, node.private_key + PrivateKey::_size); return PrivateKey(data); } -PrivateKey HDWallet::getMasterKeyExtension(TWCurve curve) const { +template +PrivateKey HDWallet::getMasterKeyExtension(TWCurve curve) const { auto node = getMasterNode(*this, curve); auto data = Data(node.private_key_extension, node.private_key_extension + PrivateKey::_size); return PrivateKey(data); } -PrivateKey HDWallet::getKey(TWCoinType coin, TWDerivation derivation) const { - const auto path = TW::derivationPath(coin, derivation); - return getKey(coin, path); -} - -DerivationPath HDWallet::cardanoStakingDerivationPath(const DerivationPath& path) { +template +DerivationPath HDWallet::cardanoStakingDerivationPath(const DerivationPath& path) { DerivationPath stakingPath = path; stakingPath.indices[3].value = 2; stakingPath.indices[4].value = 0; return stakingPath; } -PrivateKey HDWallet::getKey(TWCoinType coin, const DerivationPath& derivationPath) const { - const auto curve = TWCoinTypeCurve(coin); - return getKeyByCurve(curve, derivationPath); -} - -PrivateKey HDWallet::getKeyByCurve(TWCurve curve, const DerivationPath& derivationPath) const { +template +PrivateKey HDWallet::getKeyByCurve(TWCurve curve, const DerivationPath& derivationPath) const { const auto privateKeyType = PrivateKey::getType(curve); - auto node = getNode(*this, curve, derivationPath); + auto node = getNode(*this, curve, derivationPath); switch (privateKeyType) { case TWPrivateKeyTypeCardano: { if (derivationPath.indices.size() < 4 || derivationPath.indices[3].value > 1) { @@ -149,32 +192,50 @@ PrivateKey HDWallet::getKeyByCurve(TWCurve curve, const DerivationPath& derivati TW::memzero(&node); return PrivateKey(pkData, extData, chainCode, pkData2, extData2, chainCode2); } - case TWPrivateKeyTypeDefault: default: // default path auto data = Data(node.private_key, node.private_key + PrivateKey::_size); TW::memzero(&node); + if (curve == TWCurveStarkex) { + return ImmutableX::getPrivateKeyFromEthPrivKey(PrivateKey(data)); + } return PrivateKey(data); } } -std::string HDWallet::getRootKey(TWCoinType coin, TWHDVersion version) const { +template +PrivateKey HDWallet::getKey(TWCoinType coin, const DerivationPath& derivationPath) const { const auto curve = TWCoinTypeCurve(coin); - auto node = getMasterNode(*this, curve); - return serialize(&node, 0, version, false, base58Hasher(coin)); + return getKeyByCurve(curve, derivationPath); } -std::string HDWallet::deriveAddress(TWCoinType coin) const { - return deriveAddress(coin, TWDerivationDefault); +template +PrivateKey HDWallet::getKey(TWCoinType coin, TWDerivation derivation) const { + const auto path = TW::derivationPath(coin, derivation); + return getKey(coin, path); } -std::string HDWallet::deriveAddress(TWCoinType coin, TWDerivation derivation) const { +template +std::string HDWallet::getRootKey(TWCoinType coin, TWHDVersion version) const { + const auto curve = TWCoinTypeCurve(coin); + auto node = getMasterNode(*this, curve); + return serialize(&node, 0, version, false, base58Hasher(coin)); +} + +template +std::string HDWallet::deriveAddress(TWCoinType coin, TWDerivation derivation) const { const auto derivationPath = TW::derivationPath(coin, derivation); return TW::deriveAddress(coin, getKey(coin, derivationPath), derivation); } -std::string HDWallet::getExtendedPrivateKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const { +template +std::string HDWallet::deriveAddress(TWCoinType coin) const { + return deriveAddress(coin, TWDerivationDefault); +} + +template +std::string HDWallet::getExtendedPrivateKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const { if (version == TWHDVersionNone) { return ""; } @@ -188,7 +249,8 @@ std::string HDWallet::getExtendedPrivateKeyAccount(TWPurpose purpose, TWCoinType return serialize(&node, fingerprintValue, version, false, base58Hasher(coin)); } -std::string HDWallet::getExtendedPublicKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const { +template +std::string HDWallet::getExtendedPublicKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const { if (version == TWHDVersionNone) { return ""; } @@ -203,7 +265,8 @@ std::string HDWallet::getExtendedPublicKeyAccount(TWPurpose purpose, TWCoinType return serialize(&node, fingerprintValue, version, true, base58Hasher(coin)); } -std::optional HDWallet::getPublicKeyFromExtended(const std::string& extended, TWCoinType coin, const DerivationPath& path) { +template +std::optional HDWallet::getPublicKeyFromExtended(const std::string& extended, TWCoinType coin, const DerivationPath& path) { const auto curve = TW::curve(coin); const auto hasher = TW::base58Hasher(coin); @@ -239,7 +302,8 @@ std::optional HDWallet::getPublicKeyFromExtended(const std::string& e return {}; } -std::optional HDWallet::getPrivateKeyFromExtended(const std::string& extended, TWCoinType coin, const DerivationPath& path) { +template +std::optional HDWallet::getPrivateKeyFromExtended(const std::string& extended, TWCoinType coin, const DerivationPath& path) { const auto curve = TW::curve(coin); const auto hasher = TW::base58Hasher(coin); @@ -253,6 +317,13 @@ std::optional HDWallet::getPrivateKeyFromExtended(const std::string& return PrivateKey(Data(node.private_key, node.private_key + 32)); } +template +PrivateKey HDWallet::bip32DeriveRawSeed(TWCoinType coin, const Data& seed, const DerivationPath& path) { + const auto curve = TWCoinTypeCurve(coin); + auto wallet = HDWallet(seed); + return wallet.getKeyByCurve(curve, path); +} + namespace { uint32_t fingerprint(HDNode* node, Hash::Hasher hasher) { @@ -311,47 +382,9 @@ bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher return true; } -HDNode getNode(const HDWallet& wallet, TWCurve curve, const DerivationPath& derivationPath) { - const auto privateKeyType = PrivateKey::getType(curve); - auto node = getMasterNode(wallet, curve); - for (auto& index : derivationPath.indices) { - switch (privateKeyType) { - case TWPrivateKeyTypeCardano: - hdnode_private_ckd_cardano(&node, index.derivationIndex()); - break; - case TWPrivateKeyTypeDefault: - default: - hdnode_private_ckd(&node, index.derivationIndex()); - break; - } - } - return node; -} - -HDNode getMasterNode(const HDWallet& wallet, TWCurve curve) { - const auto privateKeyType = PrivateKey::getType(curve); - HDNode node; - switch (privateKeyType) { - case TWPrivateKeyTypeCardano: { - // Derives the root Cardano HDNode from a passphrase and the entropy encoded in - // a BIP-0039 mnemonic using the Icarus derivation (V2) scheme - const auto entropy = wallet.getEntropy(); - uint8_t secret[CARDANO_SECRET_LENGTH]; - secret_from_entropy_cardano_icarus((const uint8_t*)"", 0, entropy.data(), int(entropy.size()), secret, nullptr); - hdnode_from_secret_cardano(secret, &node); - TW::memzero(secret, CARDANO_SECRET_LENGTH); - break; - } - case TWPrivateKeyTypeDefault: - default: - hdnode_from_seed(wallet.getSeed().data(), HDWallet::seedSize, curveName(curve), &node); - break; - } - return node; -} - const char* curveName(TWCurve curve) { switch (curve) { + case TWCurveStarkex: case TWCurveSECP256k1: return SECP256K1_NAME; case TWCurveED25519: @@ -371,3 +404,8 @@ const char* curveName(TWCurve curve) { } } // namespace + +namespace TW { +template class HDWallet<32>; +template class HDWallet<64>; +} // namespace TW diff --git a/src/HDWallet.h b/src/HDWallet.h index 0579591c82e..38c928e451b 100644 --- a/src/HDWallet.h +++ b/src/HDWallet.h @@ -24,9 +24,10 @@ namespace TW { +template class HDWallet { public: - static constexpr size_t seedSize = 64; + static constexpr size_t mSeedSize = seedSize; static constexpr size_t maxMnemomincSize = 240; static constexpr size_t maxExtendedKeySize = 128; @@ -43,13 +44,16 @@ class HDWallet { /// Entropy is the binary 1-to-1 representation of the mnemonic (11 bits from each word) TW::Data entropy; - public: +public: const std::array& getSeed() const { return seed; } const std::string& getMnemonic() const { return mnemonic; } const std::string& getPassphrase() const { return passphrase; } const TW::Data& getEntropy() const { return entropy; } public: + /// Initializes an HDWallet from given seed. + HDWallet(const Data& seed); + /// Initializes a new random HDWallet with the provided strength in bits. /// Throws on invalid strength. HDWallet(int strength, const std::string& passphrase); @@ -125,6 +129,13 @@ class HDWallet { /// Computes the private key from an extended private key representation. static std::optional getPrivateKeyFromExtended(const std::string& extended, TWCoinType coin, const DerivationPath& path); + /// Derive the given seed for the given coin, with the given Derivation path + /// \param coin Coin to be used in order to retrieve the curve type + /// \param seed Custom seed to be used for the derivation, can be a mnemonic seed as well as an ethereum signature seed + /// \param path The derivation path to use + /// \return The computed private key + static PrivateKey bip32DeriveRawSeed(TWCoinType coin, const Data& seed, const DerivationPath& path); + private: void updateSeedAndEntropy(bool check = true); @@ -136,5 +147,5 @@ class HDWallet { /// Wrapper for C interface. struct TWHDWallet { - TW::HDWallet impl; + TW::HDWallet<> impl; }; diff --git a/src/HexCoding.h b/src/HexCoding.h index 6a12f0e2456..8cf121ae3e0 100644 --- a/src/HexCoding.h +++ b/src/HexCoding.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -23,8 +24,7 @@ template inline std::string hex(const Iter begin, const Iter end) { static constexpr std::array hexmap = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; std::string result; result.reserve((end - begin) * 2); @@ -94,4 +94,51 @@ inline Data parse_hex(const std::string& string, bool padLeft = false) { return parse_hex(string.begin(), string.end()); } +inline const char* hex_char_to_bin(char c) { + switch (toupper(c)) { + case '0': + return "0000"; + case '1': + return "0001"; + case '2': + return "0010"; + case '3': + return "0011"; + case '4': + return "0100"; + case '5': + return "0101"; + case '6': + return "0110"; + case '7': + return "0111"; + case '8': + return "1000"; + case '9': + return "1001"; + case 'A': + return "1010"; + case 'B': + return "1011"; + case 'C': + return "1100"; + case 'D': + return "1101"; + case 'E': + return "1110"; + case 'F': + return "1111"; + default: + return ""; + } +} + +inline std::string hex_str_to_bin_str(const std::string& hex) { + std::stringstream ss; + for (auto&& c: hex) { + ss << hex_char_to_bin(c); + } + return ss.str(); +} + } // namespace TW diff --git a/src/ImmutableX/Constants.h b/src/ImmutableX/Constants.h new file mode 100644 index 00000000000..f70a3565475 --- /dev/null +++ b/src/ImmutableX/Constants.h @@ -0,0 +1,24 @@ +// Copyright © 2017-2022 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 "uint256.h" + +namespace TW::ImmutableX { + +namespace internal { +inline constexpr const char* gLayer = "starkex"; +inline constexpr const char* gApplication = "immutablex"; +inline constexpr const char* gIndex = "1"; +inline const int256_t gStarkCurveA(1); +inline const int256_t gStarkCurveB("3141592653589793238462643383279502884197169399375105820974944592307816406665"); +inline const int256_t gStarkCurveN("3618502788666131213697322783095070105526743751716087489154079457884512865583"); +inline const int256_t gStarkCurveP("3618502788666131213697322783095070105623107215331596699973092056135872020481"); +inline const int256_t gStarkDeriveBias("112173586448650067624617006275947173271329056303198712163776463194419898833073"); +} // namespace internal + +} // namespace TW::ImmutableX diff --git a/src/ImmutableX/StarkKey.cpp b/src/ImmutableX/StarkKey.cpp new file mode 100644 index 00000000000..b2bd205f735 --- /dev/null +++ b/src/ImmutableX/StarkKey.cpp @@ -0,0 +1,83 @@ +// Copyright © 2017-2022 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 +#include +#include +#include +#include +#include +#include +#include + +namespace TW::ImmutableX { + +uint256_t hashKeyWithIndex(const Data& seed, std::size_t index) { + auto data = seed; + data.push_back(static_cast(index)); + auto out = Hash::sha256(data); + return load(out); +} + +std::string grindKey(const Data& seed) { + std::size_t index{0}; + int256_t key = hashKeyWithIndex(seed, index); + while (key >= internal::gStarkDeriveBias) { + key = hashKeyWithIndex(store(uint256_t(key)), index); + index += 1; + } + auto finalKey = key % internal::gStarkCurveN; + std::stringstream ss; + ss << std::hex << finalKey; + return ss.str(); +} + +PrivateKey getPrivateKeyFromSeed(const Data& seed, const DerivationPath& path) { + auto key = HDWallet<32>::bip32DeriveRawSeed(TWCoinTypeEthereum, seed, path); + auto data = parse_hex(grindKey(key.bytes), true); + return PrivateKey(data); +} + +PrivateKey getPrivateKeyFromEthPrivKey(const PrivateKey& ethPrivKey) { + return PrivateKey(parse_hex(ImmutableX::grindKey(ethPrivKey.bytes), true)); +} + +PrivateKey getPrivateKeyFromRawSignature(const Data& signature, const DerivationPath& derivationPath) { + using namespace internal; + //auto data = parse_hex(signature); + auto ethSignature = Ethereum::Signer::signatureDataToStructSimple(signature); + auto seed = store(ethSignature.s); + return getPrivateKeyFromSeed(seed, derivationPath); +} + +Data getPublicKeyFromPrivateKey(const Data& privateKey) { + auto pubKey = starknet_pubkey_from_private(hex(privateKey).c_str()); + std::string pubKeyStr = pubKey; + free_string(pubKey); + return parse_hex(pubKeyStr, true); +} + +Data sign(const Data& privateKey, const Data& digest) { + auto privKeyStr = hex(privateKey); + auto hexDigest = hex(digest); + auto resultSignature = starknet_sign(privKeyStr.c_str(), hexDigest.c_str()); + auto toReturn = parse_hex(resultSignature); + free_string(resultSignature); + return toReturn; +} + +bool verify(const Data& pubKey, const Data& signature, const Data& digest) { + if (signature.size() != 64) { + return false; + } + auto r = hex(subData(signature, 0, 32)); + auto s = hex(subData(signature, 32)); + auto pubKeyStr = hex(pubKey); + auto digestStr = hex(digest); + return starknet_verify(pubKeyStr.c_str(), digestStr.c_str(), r.c_str(), s.c_str()); +} + +} // namespace TW::ImmutableX diff --git a/src/ImmutableX/StarkKey.h b/src/ImmutableX/StarkKey.h new file mode 100644 index 00000000000..2bbeab1d7d2 --- /dev/null +++ b/src/ImmutableX/StarkKey.h @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 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 "uint256.h" +#include +#include +#include + +namespace TW::ImmutableX { + +uint256_t hashKeyWithIndex(const Data& seed, std::size_t index); + +std::string grindKey(const Data& seed); + +PrivateKey getPrivateKeyFromSeed(const std::string& seed, const DerivationPath& path); + +PrivateKey getPrivateKeyFromEthPrivKey(const PrivateKey& ethPrivKey); + +PrivateKey getPrivateKeyFromRawSignature(const Data& signature, const DerivationPath& derivationPath); + +Data getPublicKeyFromPrivateKey(const Data& privateKey); + +Data sign(const Data &privateKey, const Data& digest); + +bool verify(const Data &pubKey, const Data& signature, const Data& digest); + +} // namespace TW::ImmutableX diff --git a/src/Keystore/StoredKey.cpp b/src/Keystore/StoredKey.cpp index 9b761186ce4..bc6d9597981 100644 --- a/src/Keystore/StoredKey.cpp +++ b/src/Keystore/StoredKey.cpp @@ -36,7 +36,7 @@ StoredKey StoredKey::createWithMnemonic(const std::string& name, const Data& pas } StoredKey StoredKey::createWithMnemonicRandom(const std::string& name, const Data& password, TWStoredKeyEncryptionLevel encryptionLevel, TWStoredKeyEncryption encryption) { - const auto wallet = TW::HDWallet(128, ""); + const auto wallet = TW::HDWallet<>(128, ""); const auto& mnemonic = wallet.getMnemonic(); assert(Mnemonic::isValid(mnemonic)); Data mnemonicData = TW::Data(mnemonic.begin(), mnemonic.end()); @@ -77,13 +77,13 @@ StoredKey::StoredKey(StoredKeyType type, std::string name, const Data& password, id = boost::lexical_cast(gen()); } -const HDWallet StoredKey::wallet(const Data& password) const { +const HDWallet<> StoredKey::wallet(const Data& password) const { if (type != StoredKeyType::mnemonicPhrase) { throw std::invalid_argument("Invalid account requested."); } const auto data = payload.decrypt(password); const auto mnemonic = std::string(reinterpret_cast(data.data()), data.size()); - return HDWallet(mnemonic, ""); + return HDWallet<>(mnemonic, ""); } std::vector StoredKey::getAccounts(TWCoinType coin) const { @@ -96,7 +96,7 @@ std::vector StoredKey::getAccounts(TWCoinType coin) const { return result; } -std::optional StoredKey::getDefaultAccount(TWCoinType coin, const HDWallet* wallet) const { +std::optional StoredKey::getDefaultAccount(TWCoinType coin, const HDWallet<>* wallet) const { // there are multiple, try to look for default if (wallet != nullptr) { const auto address = wallet->deriveAddress(coin); @@ -115,7 +115,7 @@ std::optional StoredKey::getDefaultAccount(TWCoinType coin, const HDWal return std::nullopt; } -std::optional StoredKey::getDefaultAccountOrAny(TWCoinType coin, const HDWallet* wallet) const { +std::optional StoredKey::getDefaultAccountOrAny(TWCoinType coin, const HDWallet<>* wallet) const { const auto defaultAccount = getDefaultAccount(coin, wallet); if (defaultAccount.has_value()) { return defaultAccount; @@ -137,13 +137,13 @@ std::optional StoredKey::getAccount(TWCoinType coin, const std::string& return std::nullopt; } -std::optional StoredKey::getAccount(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) const { +std::optional StoredKey::getAccount(TWCoinType coin, TWDerivation derivation, const HDWallet<>& wallet) const { // obtain address const auto address = wallet.deriveAddress(coin, derivation); return getAccount(coin, address); } -Account StoredKey::fillAddressIfMissing(Account& account, const HDWallet* wallet) const { +Account StoredKey::fillAddressIfMissing(Account& account, const HDWallet<>* wallet) const { if (account.address.empty() && wallet != nullptr) { account.address = wallet->deriveAddress(account.coin, account.derivation); } @@ -155,7 +155,7 @@ Account StoredKey::fillAddressIfMissing(Account& account, const HDWallet* wallet return account; } -std::optional StoredKey::account(TWCoinType coin, const HDWallet* wallet) { +std::optional StoredKey::account(TWCoinType coin, const HDWallet<>* wallet) { const auto account = getDefaultAccountOrAny(coin, wallet); if (account.has_value()) { Account accountLval = account.value(); @@ -177,7 +177,7 @@ std::optional StoredKey::account(TWCoinType coin, const HDWallet* return accounts.back(); } -Account StoredKey::account(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) { +Account StoredKey::account(TWCoinType coin, TWDerivation derivation, const HDWallet<>& wallet) { const auto coinAccount = getAccount(coin, derivation, wallet); if (coinAccount.has_value()) { Account accountLval = coinAccount.value(); @@ -199,7 +199,7 @@ std::optional StoredKey::account(TWCoinType coin) const { return getDefaultAccountOrAny(coin, nullptr); } -std::optional StoredKey::account(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) const { +std::optional StoredKey::account(TWCoinType coin, TWDerivation derivation, const HDWallet<>& wallet) const { const auto account = getAccount(coin, derivation, wallet); if (account.has_value()) { Account accountLval = account.value(); diff --git a/src/Keystore/StoredKey.h b/src/Keystore/StoredKey.h index 58a5dca3f77..7ff5e071276 100644 --- a/src/Keystore/StoredKey.h +++ b/src/Keystore/StoredKey.h @@ -70,25 +70,25 @@ class StoredKey { /// Returns the HDWallet for this key. /// /// @throws std::invalid_argument if this key is of a type other than `mnemonicPhrase`. - const HDWallet wallet(const Data& password) const; + const HDWallet<> wallet(const Data& password) const; /// Returns all the accounts for a specific coin: 0, 1, or more. std::vector getAccounts(TWCoinType coin) const; /// If found, returns the account for a specific coin. In case of muliple accounts, the default derivation is returned, or the first one is returned. /// If none exists, and wallet is not null, an account is created (with default derivation). - std::optional account(TWCoinType coin, const HDWallet* wallet); + std::optional account(TWCoinType coin, const HDWallet<>* wallet); /// If found, returns the account for a specific coin and derivation. In case of muliple accounts, the first one is returned. /// If none exists, an account is created. - Account account(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet); + Account account(TWCoinType coin, TWDerivation derivation, const HDWallet<>& wallet); /// Returns the account for a specific coin if it exists. /// In case of muliple accounts, the default derivation is returned, or the first one is returned. std::optional account(TWCoinType coin) const; /// Returns the account for a specific coin and derivation, if it exists. - std::optional account(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) const; + std::optional account(TWCoinType coin, TWDerivation derivation, const HDWallet<>& wallet) const; /// Add an account with aribitrary address/derivation path. Discouraged, use account() versions. /// Address must be unique (for a coin). @@ -157,20 +157,20 @@ class StoredKey { /// Find default account for coin, if exists. If multiple exist, default is returned. /// Optional wallet is needed to derive default address - std::optional getDefaultAccount(TWCoinType coin, const HDWallet* wallet) const; + std::optional getDefaultAccount(TWCoinType coin, const HDWallet<>* wallet) const; /// Find account for coin, if exists. If multiple exist, default is returned, or any. /// Optional wallet is needed to derive default address - std::optional getDefaultAccountOrAny(TWCoinType coin, const HDWallet* wallet) const; + std::optional getDefaultAccountOrAny(TWCoinType coin, const HDWallet<>* wallet) const; /// Find account by coin+address (should be one, if multiple, first is returned) std::optional getAccount(TWCoinType coin, const std::string& address) const; /// Find account by coin+derivation (should be one, if multiple, first is returned) - std::optional getAccount(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) const; + std::optional getAccount(TWCoinType coin, TWDerivation derivation, const HDWallet<>& wallet) const; /// Re-derive account address if missing - Account fillAddressIfMissing(Account& account, const HDWallet* wallet) const; + Account fillAddressIfMissing(Account& account, const HDWallet<>* wallet) const; }; } // namespace TW::Keystore diff --git a/src/PrivateKey.cpp b/src/PrivateKey.cpp index 898953100ed..b11cd18fadb 100644 --- a/src/PrivateKey.cpp +++ b/src/PrivateKey.cpp @@ -6,6 +6,7 @@ #include "PrivateKey.h" +#include "HexCoding.h" #include "PublicKey.h" #include @@ -18,6 +19,7 @@ #include #include #include +#include #include @@ -152,12 +154,21 @@ PublicKey PrivateKey::getPublicKey(TWPublicKeyType type) const { append(result, secondChainCode()); } break; - case TWPublicKeyTypeCURVE25519: + case TWPublicKeyTypeCURVE25519: { result.resize(PublicKey::ed25519Size); PublicKey ed25519PublicKey = getPublicKey(TWPublicKeyTypeED25519); ed25519_pk_to_curve25519(result.data(), ed25519PublicKey.bytes.data()); break; } + + case TWPublicKeyTypeStarkex: { + result = ImmutableX::getPublicKeyFromPrivateKey(this->bytes); + if (result.size() == PublicKey::starkexSize - 1) { + result.insert(result.begin(), 0); + } + break; + } + } return PublicKey(result, type); } @@ -223,6 +234,11 @@ Data PrivateKey::sign(const Data& digest, TWCurve curve) const { result.resize(65); success = ecdsa_sign_digest_checked(&nist256p1, key().data(), digest.data(), digest.size(), result.data(), result.data() + 64, nullptr) == 0; } break; + case TWCurveStarkex: { + result = ImmutableX::sign(this->bytes, digest); + success = true; + break; + } case TWCurveNone: default: break; diff --git a/src/PublicKey.cpp b/src/PublicKey.cpp index 0fcd53c3a3b..c002c4d1320 100644 --- a/src/PublicKey.cpp +++ b/src/PublicKey.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -41,6 +42,8 @@ bool PublicKey::isValid(const Data& data, enum TWPublicKeyType type) { case TWPublicKeyTypeSECP256k1Extended: case TWPublicKeyTypeNIST256p1Extended: return size == secp256k1ExtendedSize && data[0] == 0x04; + case TWPublicKeyTypeStarkex: + return size == starkexSize; default: return false; } @@ -55,6 +58,7 @@ PublicKey::PublicKey(const Data& data, enum TWPublicKeyType type) throw std::invalid_argument("Invalid public key data"); } switch (type) { + case TWPublicKeyTypeStarkex: case TWPublicKeyTypeSECP256k1: case TWPublicKeyTypeNIST256p1: case TWPublicKeyTypeSECP256k1Extended: @@ -158,6 +162,8 @@ bool PublicKey::verify(const Data& signature, const Data& message) const { verifyBuffer[63] &= 127; return ed25519_sign_open(message.data(), message.size(), ed25519PublicKey.data(), verifyBuffer.data()) == 0; } + case TWPublicKeyTypeStarkex: + return ImmutableX::verify(this->bytes, signature, message); default: throw std::logic_error("Not yet implemented"); } diff --git a/src/PublicKey.h b/src/PublicKey.h index 111f0fdf306..d5698c6e89d 100644 --- a/src/PublicKey.h +++ b/src/PublicKey.h @@ -24,6 +24,9 @@ class PublicKey { /// The number of bytes in an ed25519 public key. static const size_t ed25519Size = 32; + /// The number of bytes in an starkex public key. + static const size_t starkexSize = 32; + /// The number of bytes in a Cardano public key (two ed25519 public key + chain code). static const size_t cardanoKeySize = 2 * 2 * 32; diff --git a/src/StarkEx/MessageSigner.cpp b/src/StarkEx/MessageSigner.cpp new file mode 100644 index 00000000000..b6c5b96cb5c --- /dev/null +++ b/src/StarkEx/MessageSigner.cpp @@ -0,0 +1,23 @@ +// Copyright © 2017-2022 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 +#include + +namespace TW::StarkEx { + +std::string MessageSigner::signMessage(const TW::PrivateKey& privateKey, const std::string& message) { + auto digest = parse_hex(message, true); + return hex(privateKey.sign(digest, TWCurveStarkex)); +} + +bool MessageSigner::verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept { + auto starkSignature = parse_hex(signature, true); + auto digest = parse_hex(message, true); + return publicKey.verify(starkSignature, digest); +} + +} // namespace TW::StarkEx diff --git a/src/StarkEx/MessageSigner.h b/src/StarkEx/MessageSigner.h new file mode 100644 index 00000000000..1face6ccc08 --- /dev/null +++ b/src/StarkEx/MessageSigner.h @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 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 +#include + +namespace TW::StarkEx { +class MessageSigner { +public: + /// Sign a message following StarkEx Curve + /// \param privateKey the private key to sign with + /// \param message hex message to sign + /// \return hex signed message + static std::string signMessage(const PrivateKey& privateKey, const std::string& message);; + + /// Verify a message following EIP-191 + /// \param publicKey publickey to verify the signed message + /// \param message message to be verified as a string + /// \param signature signature to verify the message against + /// \return true if the message match the signature, false otherwise + static bool verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept;; +}; +} diff --git a/src/XRP/Transaction.cpp b/src/XRP/Transaction.cpp index 25b6625aaef..2358fb723a3 100644 --- a/src/XRP/Transaction.cpp +++ b/src/XRP/Transaction.cpp @@ -9,6 +9,7 @@ #include "../BinaryCoding.h" #include +#include #include #include @@ -168,8 +169,8 @@ Data Transaction::serializeCurrencyAmount(const CurrencyAmount& currency_amount) return Data(); } - int64_t min_mantissa = (uint64_t)pow(10, 15); - int64_t max_mantissa = (uint64_t)pow(10, 16) - 1; + int64_t min_mantissa = (uint64_t)std::pow(10, 15); + int64_t max_mantissa = (uint64_t)std::pow(10, 16) - 1; int32_t min_exp = -96; int32_t max_exp = 80; diff --git a/src/interface/TWEthereumEip2645.cpp b/src/interface/TWEthereumEip2645.cpp new file mode 100644 index 00000000000..21a9082573a --- /dev/null +++ b/src/interface/TWEthereumEip2645.cpp @@ -0,0 +1,18 @@ +// Copyright © 2017-2022 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 +#include "Ethereum/EIP2645.h" + +#include + +TWString* TWEthereumEip2645GetPath(TWString* ethAddress, TWString* layer, TWString* application, TWString* index) { + const auto& ethAddressStr = *reinterpret_cast(ethAddress); + const auto& layerStr = *reinterpret_cast(layer); + const auto& applicationStr = *reinterpret_cast(application); + const auto& indexStr = *reinterpret_cast(index); + return new std::string(TW::Ethereum::accountPathFromAddress(ethAddressStr, layerStr, applicationStr, indexStr)); +} diff --git a/src/interface/TWEthereumMessageSigner.cpp b/src/interface/TWEthereumMessageSigner.cpp new file mode 100644 index 00000000000..19e1d6e3705 --- /dev/null +++ b/src/interface/TWEthereumMessageSigner.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 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 + +#include "Ethereum/EIP191.h" + +TWString* _Nonnull TWEthereumMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message) { + try { + const auto signature = TW::Ethereum::MessageSigner::signMessage(privateKey->impl, TWStringUTF8Bytes(message)); + return TWStringCreateWithUTF8Bytes(signature.c_str()); + } catch (...) { + return TWStringCreateWithUTF8Bytes(""); + } +} + +bool TWEthereumMessageSignerVerifyMessage(const struct TWPublicKey* _Nonnull publicKey, TWString* _Nonnull message, TWString* _Nonnull signature) { + return TW::Ethereum::MessageSigner::verifyMessage(publicKey->impl, TWStringUTF8Bytes(message), TWStringUTF8Bytes(signature)); +} diff --git a/src/interface/TWHDWallet.cpp b/src/interface/TWHDWallet.cpp index 630b0613110..3be263eb5ee 100644 --- a/src/interface/TWHDWallet.cpp +++ b/src/interface/TWHDWallet.cpp @@ -51,7 +51,7 @@ void TWHDWalletDelete(struct TWHDWallet *wallet) { } TWData *_Nonnull TWHDWalletSeed(struct TWHDWallet *_Nonnull wallet) { - return TWDataCreateWithBytes(wallet->impl.getSeed().data(), HDWallet::seedSize); + return TWDataCreateWithBytes(wallet->impl.getSeed().data(), HDWallet<>::mSeedSize); } TWString *_Nonnull TWHDWalletMnemonic(struct TWHDWallet *_Nonnull wallet){ @@ -129,7 +129,7 @@ TWString *_Nonnull TWHDWalletGetExtendedPublicKeyDerivation(struct TWHDWallet *w TWPublicKey *TWHDWalletGetPublicKeyFromExtended(TWString *_Nonnull extended, enum TWCoinType coin, TWString *_Nonnull derivationPath) { const auto derivationPathObject = DerivationPath(*reinterpret_cast(derivationPath)); - auto publicKey = HDWallet::getPublicKeyFromExtended(*reinterpret_cast(extended), coin, derivationPathObject); + auto publicKey = HDWallet<>::getPublicKeyFromExtended(*reinterpret_cast(extended), coin, derivationPathObject); if (!publicKey) { return nullptr; } diff --git a/src/interface/TWStarkExMessageSigner.cpp b/src/interface/TWStarkExMessageSigner.cpp new file mode 100644 index 00000000000..04502acf234 --- /dev/null +++ b/src/interface/TWStarkExMessageSigner.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 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 + +#include "StarkEx/MessageSigner.h" + +TWString* _Nonnull TWStarkExMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message) { + try { + const auto signature = TW::StarkEx::MessageSigner::signMessage(privateKey->impl, TWStringUTF8Bytes(message)); + return TWStringCreateWithUTF8Bytes(signature.c_str()); + } catch (...) { + return TWStringCreateWithUTF8Bytes(""); + } +} + +bool TWStarkExMessageSignerVerifyMessage(const struct TWPublicKey* _Nonnull publicKey, TWString* _Nonnull message, TWString* _Nonnull signature) { + return TW::StarkEx::MessageSigner::verifyMessage(publicKey->impl, TWStringUTF8Bytes(message), TWStringUTF8Bytes(signature)); +} diff --git a/src/interface/TWStarkWare.cpp b/src/interface/TWStarkWare.cpp new file mode 100644 index 00000000000..ede9644fa04 --- /dev/null +++ b/src/interface/TWStarkWare.cpp @@ -0,0 +1,17 @@ +// Copyright © 2017-2022 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 +#include +#include +#include +#include + +struct TWPrivateKey* TWStarkWareGetStarkKeyFromSignature(const struct TWDerivationPath* derivationPath, TWString* signature) { + using namespace TW; + const auto& ethSignatureStr = *reinterpret_cast(signature); + return new TWPrivateKey{ ImmutableX::getPrivateKeyFromRawSignature(parse_hex(ethSignatureStr), derivationPath->impl)}; +} diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index c3091e428b6..e53e1cdebb1 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -274,4 +274,13 @@ class EthereumTests: XCTestCase { XCTAssertEqual(ethAddress, "0xa4531dE99E22B2166d340E7221669DF565c52024") XCTAssertEqual(btcAddress, "bc1q97jc0jdgsyvvhxydxxd6np8sa920c39l3qpscf") } + + func testMessageAndVerifySigner() { + let privateKey = PrivateKey(data: Data(hexString: "3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84")!)! + let msg = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3" + let signature = EthereumMessageSigner.signMessage(privateKey: privateKey, message: msg) + XCTAssertEqual(signature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01") + let pubKey = privateKey.getPublicKey(coinType: .ethereum) + XCTAssertTrue(EthereumMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) + } } diff --git a/swift/Tests/Blockchains/StarkExTests.swift b/swift/Tests/Blockchains/StarkExTests.swift new file mode 100644 index 00000000000..88e868ab10c --- /dev/null +++ b/swift/Tests/Blockchains/StarkExTests.swift @@ -0,0 +1,19 @@ +// Copyright © 2017-2022 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. + +import XCTest +import WalletCore + +class StarkExTests: XCTestCase { + func testMessageAndVerifySigner() { + let privateKey = PrivateKey(data: Data(hexString: "04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de")!)! + let msg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf" + let signature = StarkExMessageSigner.signMessage(privateKey: privateKey, message: msg) + XCTAssertEqual(signature, "04cf5f21333dd189ada3c0f2a51430d733501a9b1d5e07905273c1938cfb261e05b6013d74adde403e8953743a338c8d414bb96bf69d2ca1a91a85ed2700a528") + let pubKey = privateKey.getPublicKeyByType(pubkeyType: .starkex) + XCTAssertTrue(StarkExMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) + } +} diff --git a/swift/Tests/HDWalletTests.swift b/swift/Tests/HDWalletTests.swift index cc832c55a7e..6dccac4705f 100644 --- a/swift/Tests/HDWalletTests.swift +++ b/swift/Tests/HDWalletTests.swift @@ -12,6 +12,38 @@ extension HDWallet { } class HDWalletTests: XCTestCase { + + func testFromMnemonicImmutableXMainnetFromSignature() { + let wallet = HDWallet(mnemonic: "obscure opera favorite shuffle mail tip age debate dirt pact cement loyal", passphrase: "")! + let starkDerivationPath = EthereumEip2645.getPath(ethAddress: "0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37", layer: "starkex", application: "immutablex", index: "1") + XCTAssertEqual(starkDerivationPath, "m/2645'/579218131'/211006541'/2124474935'/1609799702'/1") + + // Retrieve eth private key + let ethPrivateKey = wallet.getKeyForCoin(coin: CoinType.ethereum) + XCTAssertEqual(ethPrivateKey.data.hexString, "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"); + + // StarkKey Derivation Path + let derivationPath = DerivationPath(string: starkDerivationPath)! + + // Retrieve Stark Private key part + let ethMsg = "Only sign this request if you’ve initiated an action with Immutable X." + let ethSignature = EthereumMessageSigner.signMessage(privateKey: ethPrivateKey, message: ethMsg) + XCTAssertEqual(ethSignature, "18b1be8b78807d3326e28bc286d7ee3d068dcd90b1949ce1d25c1f99825f26e70992c5eb7f44f76b202aceded00d74f771ed751f2fe538eec01e338164914fe001") + let starkPrivateKey = StarkWare.getStarkKeyFromSignature(derivationPath: derivationPath, signature: ethSignature) + XCTAssertEqual(starkPrivateKey.data.hexString, "04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de") + let starkPublicKey = starkPrivateKey.getPublicKeyByType(pubkeyType: .starkex) + XCTAssertEqual(starkPublicKey.data.hexString, "00e5b9b11f8372610ef35d647a1dcaba1a4010716588d591189b27bf3c2d5095") + + // Account Register + let ethMsgToRegister = "Only sign this key linking request from Immutable X" + let ethSignatureToRegister = EthereumMessageSigner.signMessage(privateKey: ethPrivateKey, message: ethMsgToRegister) + XCTAssertEqual(ethSignatureToRegister, "646da4160f7fc9205e6f502fb7691a0bf63ecbb74bbb653465cd62388dd9f56325ab1e4a9aba99b1661e3e6251b42822855a71e60017b310b9f90e990a12e1dc01") + let starkMsg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf" + let starkSignature = StarkExMessageSigner.signMessage(privateKey: starkPrivateKey, message: starkMsg) + XCTAssertEqual(starkSignature, "04cf5f21333dd189ada3c0f2a51430d733501a9b1d5e07905273c1938cfb261e05b6013d74adde403e8953743a338c8d414bb96bf69d2ca1a91a85ed2700a528") + XCTAssertTrue(StarkExMessageSigner.verifyMessage(pubKey: starkPublicKey, message: starkMsg, signature: starkSignature)) + } + func testCreateFromMnemonic() { let wallet = HDWallet.test diff --git a/swift/Tests/PrivateKeyTests.swift b/swift/Tests/PrivateKeyTests.swift index ae363969f17..bc9ae7e13ff 100644 --- a/swift/Tests/PrivateKeyTests.swift +++ b/swift/Tests/PrivateKeyTests.swift @@ -20,6 +20,22 @@ class PrivateKeyTests: XCTestCase { let privateKey = PrivateKey(data: Data(hexString: "0xdeadbeef")!) XCTAssertNil(privateKey) } + + func testStarkKeyCreation() { + let data = Data(hexString: "06cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c")! + XCTAssertTrue(PrivateKey.isValid(data: data, curve: .starkex)) + let privateKey = PrivateKey(data: data)! + let pubKey = privateKey.getPublicKeyByType(pubkeyType: .starkex) + XCTAssertEqual(pubKey.data.hexString, "02d2bbdc1adaf887b0027cdde2113cfd81c60493aa6dc15d7887ddf1a82bc831") + } + + func testStarkKeySigning() { + let data = Data(hexString: "0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79")! + let privateKey = PrivateKey(data: data)! + let digest = Data(hexString: "06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76")! + let signature = privateKey.sign(digest: digest, curve: .starkex)! + XCTAssertEqual(signature.hexString, "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a") + } func testIsValidString() { let data = Data(hexString: "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")! diff --git a/swift/Tests/PublicKeyTests.swift b/swift/Tests/PublicKeyTests.swift index b6844ea38c8..5819e3d3ed0 100644 --- a/swift/Tests/PublicKeyTests.swift +++ b/swift/Tests/PublicKeyTests.swift @@ -35,4 +35,14 @@ class PublicKeyTests: XCTestCase { XCTAssertTrue(result2) XCTAssertTrue(result3) } + + func testVerifyStarkey() { + let data = Data(hexString: "02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159")! + let publicKey = PublicKey(data: data, type: .starkex)! + let signature = Data(hexString: "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a")! + let hash = Data(hexString: "06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76")! + XCTAssertTrue(publicKey.verify(signature: signature, message: hash)) + let invalidSignature = Data(hexString: "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9b")! + XCTAssertFalse(publicKey.verify(signature: invalidSignature, message: hash)) + } } diff --git a/tests/chains/Ethereum/EIP191Tests.cpp b/tests/chains/Ethereum/EIP191Tests.cpp new file mode 100644 index 00000000000..38c937479d1 --- /dev/null +++ b/tests/chains/Ethereum/EIP191Tests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 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 +#include +#include +#include +#include +#include "TestUtilities.h" + +#include + +namespace TW::Ethereum { + TEST(EthereumEip191, signMessageAndVerify) { + PrivateKey ethKey(parse_hex("3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84")); + auto msg = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3"; + auto signature = Ethereum::MessageSigner::signMessage(ethKey, msg); + ASSERT_EQ(signature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); + auto pubKey = ethKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + ASSERT_TRUE(Ethereum::MessageSigner::verifyMessage(pubKey, msg, signature)); + } + + TEST(TWEthereumMessageSigner, SignAndVerify) { + const auto privKeyData = "3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto message = STRING("Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3"); + + const auto pubKey = TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeEthereum); + const auto signature = WRAPS(TWEthereumMessageSignerSignMessage(privateKey.get(), message.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); + EXPECT_TRUE(TWEthereumMessageSignerVerifyMessage(pubKey, message.get(), signature.get())); + delete pubKey; + } +} diff --git a/tests/chains/Ethereum/SignerTests.cpp b/tests/chains/Ethereum/SignerTests.cpp index 0899ff812c0..cbb9fb38e91 100644 --- a/tests/chains/Ethereum/SignerTests.cpp +++ b/tests/chains/Ethereum/SignerTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,6 +7,7 @@ #include "Ethereum/Address.h" #include "Ethereum/RLP.h" #include "Ethereum/Signer.h" +#include "Ethereum/EIP191.h" #include "Ethereum/Transaction.h" #include "HexCoding.h" @@ -16,7 +17,6 @@ namespace TW::Ethereum { using boost::multiprecision::uint256_t; - TEST(EthereumTransaction, encodeTransactionNonTyped) { const auto transaction = TransactionNonTyped::buildERC20Transfer( /* nonce: */ 0, diff --git a/tests/chains/ImmutableX/StarkKeyTests.cpp b/tests/chains/ImmutableX/StarkKeyTests.cpp new file mode 100644 index 00000000000..677c3ef5d34 --- /dev/null +++ b/tests/chains/ImmutableX/StarkKeyTests.cpp @@ -0,0 +1,108 @@ +// Copyright © 2017-2022 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 "Ethereum/EIP2645.h" +#include "Ethereum/Signer.h" +#include "HexCoding.h" +#include "ImmutableX/Constants.h" +#include "ImmutableX/StarkKey.h" +#include + +namespace TW::ImmutableX::tests { + +TEST(ImmutableX, PathFromAddress) { + // https://github.com/immutable/imx-core-sdk-swift/blob/main/Tests/ImmutableXCoreTests/Crypto/Stark/StarkKeyTests.swift#L30 + auto res = Ethereum::accountPathFromAddress("0xa76e3eeb2f7143165618ab8feaabcd395b6fac7f", internal::gLayer, internal::gApplication, internal::gIndex); + ASSERT_EQ(res, "m/2645'/579218131'/211006541'/1534045311'/1431804530'/1"); +} + +TEST(ImmutableX, ExtraGrinding) { + using namespace internal; + std::string signature = "0x6d1550458c7a9a1257d73adbcf0fabc12f4497e970d9fa62dd88bf7d9e12719148c96225c1402d8707fd061b1aae2222bdf13571dfc82b3aa9974039f247f2b81b"; + std::string address = "0xa4864d977b944315389d1765ffa7e66F74ee8cd7"; + auto data = parse_hex(signature); + auto path = DerivationPath(Ethereum::accountPathFromAddress(address, gLayer, gApplication, gIndex)); + auto privKey = ImmutableX::getPrivateKeyFromRawSignature(parse_hex(signature), path); + auto pubKey = hexEncoded(getPublicKeyFromPrivateKey(privKey.bytes)); + ASSERT_EQ(pubKey, "0x035919acd61e97b3ecdc75ff8beed8d1803f7ea3cad2937926ae59cc3f8070d4"); +} + +TEST(ImmutableX, GrindKey) { + auto seed = parse_hex("86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519"); + auto res = grindKey(seed); + ASSERT_EQ(res, "5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941"); +} + +TEST(ImmutableX, GetPrivateKeySignature) { + std::string signature = "0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a4370854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c"; + auto data = parse_hex(signature); + auto ethSignature = Ethereum::Signer::signatureDataToStructSimple(data); + auto seed = store(ethSignature.r); + auto result = grindKey(seed); + ASSERT_EQ(result, "766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda"); +} + +TEST(ImmutableX, GetPrivateKeyFromSignature) { + using namespace internal; + std::string address = "0xa76e3eeb2f7143165618ab8feaabcd395b6fac7f"; + std::string signature = "0x5a263fad6f17f23e7c7ea833d058f3656d3fe464baf13f6f5ccba9a2466ba2ce4c4a250231bcac7beb165aec4c9b049b4ba40ad8dd287dc79b92b1ffcf20cdcf1b"; + auto path = DerivationPath(Ethereum::accountPathFromAddress(address, gLayer, gApplication, gIndex)); + auto privKey = ImmutableX::getPrivateKeyFromRawSignature(parse_hex(signature), path); + ASSERT_EQ(hex(privKey.bytes), "058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe"); + ASSERT_TRUE(PrivateKey::isValid(privKey.bytes)); +} + +TEST(ImmutableX, GetPublicKeyFromPrivateKey) { + auto privKey = parse_hex("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe", true); + auto pubKey = hexEncoded(getPublicKeyFromPrivateKey(privKey)); + ASSERT_EQ(pubKey, "0x02a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd"); + + { + auto priv = PrivateKey(parse_hex("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe")); + auto pub = priv.getPublicKey(TWPublicKeyTypeStarkex); + ASSERT_EQ(hexEncoded(pub.bytes), "0x02a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd"); + } +} + +TEST(ImmutableX, SimpleSign) { + auto privKey = parse_hex("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79"); + auto digest = parse_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); + auto signature = hex(ImmutableX::sign(privKey, digest)); + auto expectedSignature = "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"; + ASSERT_EQ(signature.size(), 128ULL); + ASSERT_EQ(signature.substr(0, 64), "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f"); + ASSERT_EQ(signature.substr(64, 64), "04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"); + ASSERT_EQ(signature, expectedSignature); + + { + PrivateKey priv(privKey); + auto result = hex(priv.sign(digest, TWCurveStarkex)); + ASSERT_EQ(result, expectedSignature); + } +} + +TEST(ImmutableX, VerifySign) { + // valid + { + auto pubKeyData = parse_hex("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159"); + auto hash = parse_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); + auto signature = parse_hex("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"); + ASSERT_TRUE(verify(pubKeyData, signature, hash)); + auto pubKey = PublicKey(pubKeyData, TWPublicKeyTypeStarkex); + ASSERT_TRUE(pubKey.verify(signature, hash)); + } + // invalid + { + auto pubKeyData = parse_hex("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159"); + auto hash = parse_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); + auto signature = parse_hex("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9b"); + ASSERT_FALSE(verify(pubKeyData, signature, hash)); + auto pubKey = PublicKey(pubKeyData, TWPublicKeyTypeStarkex); + ASSERT_FALSE(pubKey.verify(signature, hash)); + } +} + +} // namespace TW::ImmutableX::tests diff --git a/tests/chains/StarkEx/MessageSignerTests.cpp b/tests/chains/StarkEx/MessageSignerTests.cpp new file mode 100644 index 00000000000..1399ce24ca7 --- /dev/null +++ b/tests/chains/StarkEx/MessageSignerTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 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 +#include +#include +#include +#include "TestUtilities.h" +#include + +namespace TW::StarkEx::tests { + +TEST(StarkExMessageSigner, SignAndVerify) { + PrivateKey starkPrivKey(parse_hex("04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de", true)); + auto starkPubKey = starkPrivKey.getPublicKey(TWPublicKeyTypeStarkex); + auto starkMsg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf"; + auto starkSignature = StarkEx::MessageSigner::signMessage(starkPrivKey, starkMsg); + ASSERT_EQ(starkSignature, "04cf5f21333dd189ada3c0f2a51430d733501a9b1d5e07905273c1938cfb261e05b6013d74adde403e8953743a338c8d414bb96bf69d2ca1a91a85ed2700a528"); + ASSERT_TRUE(StarkEx::MessageSigner::verifyMessage(starkPubKey, starkMsg, starkSignature)); +} + +TEST(TWStarkExMessageSigner, SignAndVerify) { + const auto privKeyData = "04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto message = STRING("463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf"); + + const auto pubKey = TWPrivateKeyGetPublicKeyByType(privateKey.get(), TWPublicKeyTypeStarkex); + const auto signature = WRAPS(TWStarkExMessageSignerSignMessage(privateKey.get(), message.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "04cf5f21333dd189ada3c0f2a51430d733501a9b1d5e07905273c1938cfb261e05b6013d74adde403e8953743a338c8d414bb96bf69d2ca1a91a85ed2700a528"); + EXPECT_TRUE(TWStarkExMessageSignerVerifyMessage(pubKey, message.get(), signature.get())); + delete pubKey; +} + +} diff --git a/tests/common/HDWallet/HDWalletInternalTests.cpp b/tests/common/HDWallet/HDWalletInternalTests.cpp index 1f27ad22cca..c4393e4affa 100644 --- a/tests/common/HDWallet/HDWalletInternalTests.cpp +++ b/tests/common/HDWallet/HDWalletInternalTests.cpp @@ -61,7 +61,7 @@ TEST(HDWalletInternal, SquareDerivationRoutes) { // getMasterNode auto masterNode = HDNode(); - hdnode_from_seed(wallet.getSeed().data(), HDWallet::seedSize, SECP256K1_NAME, &masterNode); + hdnode_from_seed(wallet.getSeed().data(), HDWallet<>::mSeedSize, SECP256K1_NAME, &masterNode); auto node0 = masterNode; // getNode diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp index 3e70073773f..f783dffb7e1 100644 --- a/tests/common/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -4,20 +4,25 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "HDWallet.h" -#include "Mnemonic.h" +#include "Base58.h" #include "Bitcoin/Address.h" #include "Bitcoin/CashAddress.h" #include "Bitcoin/SegwitAddress.h" +#include "Coin.h" #include "Ethereum/Address.h" +#include "Ethereum/Signer.h" +#include "Ethereum/EIP191.h" +#include "Ethereum/EIP2645.h" +#include "HDWallet.h" +#include "Hash.h" #include "Hedera/DER.h" -#include "NEAR/Address.h" #include "HexCoding.h" +#include "ImmutableX/StarkKey.h" +#include "Mnemonic.h" +#include "NEAR/Address.h" #include "PublicKey.h" -#include "Hash.h" -#include "Base58.h" -#include "Coin.h" #include "TestUtilities.h" +#include "StarkEx/MessageSigner.h" #include @@ -57,7 +62,7 @@ TEST(HDWallet, createFromMnemonic) { EXPECT_EQ(hex(wallet.getEntropy()), "ba5821e8c356c05ba5f025d9532fe0f21f65d594"); EXPECT_EQ(hex(wallet.getSeed()), "143cd5fc27ae46eb423efebc41610473f5e24a80f2ca2e2fa7bf167e537f58f4c68310ae487fce82e25bad29bab2530cf77fd724a5ebfc05a45872773d7ee2d6"); } - { // empty passphrase + { // empty passphrase HDWallet wallet = HDWallet(mnemonic1, ""); EXPECT_EQ(wallet.getMnemonic(), mnemonic1); EXPECT_EQ(wallet.getPassphrase(), ""); @@ -67,37 +72,37 @@ TEST(HDWallet, createFromMnemonic) { } TEST(HDWallet, entropyLength_createFromMnemonic) { - { // 12 words + { // 12 words HDWallet wallet = HDWallet("oil oil oil oil oil oil oil oil oil oil oil oil", ""); EXPECT_EQ(wallet.getEntropy().size(), 16ul); EXPECT_EQ(hex(wallet.getEntropy()), "99d33a674ce99d33a674ce99d33a674c"); } - { // 12 words, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json + { // 12 words, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json HDWallet wallet = HDWallet("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", ""); EXPECT_EQ(wallet.getEntropy().size(), 16ul); EXPECT_EQ(hex(wallet.getEntropy()), "00000000000000000000000000000000"); } - { // 15 words + { // 15 words HDWallet wallet = HDWallet("history step cheap card humble screen raise seek robot slot coral roof spoil wreck caution", ""); EXPECT_EQ(wallet.getEntropy().size(), 20ul); EXPECT_EQ(hex(wallet.getEntropy()), "6c3aac9b9146ef832c4e18bb3980c0dddd25fc49"); } - { // 18 words + { // 18 words HDWallet wallet = HDWallet("caught hockey split gun symbol code payment copy broccoli silly shed secret stove tell citizen staff photo high", ""); EXPECT_EQ(wallet.getEntropy().size(), 24ul); EXPECT_EQ(hex(wallet.getEntropy()), "246d8f48b3fdc65a2869801c791715614d6bbd8a56a0a3ad"); } - { // 21 words + { // 21 words HDWallet wallet = HDWallet("diary shine country alpha bridge coast loan hungry hip media sell crucial swarm share gospel lake visa coin dizzy physical basket", ""); EXPECT_EQ(wallet.getEntropy().size(), 28ul); EXPECT_EQ(hex(wallet.getEntropy()), "3d58bcc40381bc59a0c37a6bf14f0d9a3db78a5933e5f4a5ad00d1f1"); } - { // 24 words + { // 24 words HDWallet wallet = HDWallet("poet spider smile swift roof pilot subject save hand diet ice universe over brown inspire ugly wide economy symbol shove episode patient plug swamp", ""); EXPECT_EQ(wallet.getEntropy().size(), 32ul); EXPECT_EQ(hex(wallet.getEntropy()), "a73a3732edebbb49f5fdfe68c7b5c0f6e9de3a1d5760faa8c771e384bf4229b6"); } - { // 24 words, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json + { // 24 words, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json HDWallet wallet = HDWallet("letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", ""); EXPECT_EQ(wallet.getEntropy().size(), 32ul); EXPECT_EQ(hex(wallet.getEntropy()), "8080808080808080808080808080808080808080808080808080808080808080"); @@ -151,7 +156,7 @@ TEST(HDWallet, recreateFromEntropy) { TEST(HDWallet, privateKeyFromXPRV) { const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbdeDLiSxZt88hESHUhm2AAe2EqfWM9ucdQzH3xv1HoKoLDqHMK9n"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); ASSERT_TRUE(privateKey); auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); auto address = Bitcoin::BitcoinCashAddress(publicKey); @@ -162,7 +167,7 @@ TEST(HDWallet, privateKeyFromXPRV) { TEST(HDWallet, privateKeyFromXPRV_Invalid) { const std::string xprv = "xprv9y0000"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); ASSERT_FALSE(privateKey); } @@ -170,13 +175,13 @@ TEST(HDWallet, privateKeyFromXPRV_InvalidVersion) { { // Version bytes (first 4) are invalid, 0x00000000 const std::string xprv = "11117pE7xwz2GARukXY8Vj2ge4ozfX4HLgy5ztnJXjr5btzJE8EbtPhZwrcPWAodW2aFeYiXkXjSxJYm5QrnhSKFXDgACcFdMqGns9VLqESCq3"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); ASSERT_FALSE(privateKey); } { // Version bytes (first 4) are invalid, 0xdeadbeef const std::string xprv = "pGoh3VZXR4mTkT4bfqj4paog12KmHkAWkdLY8HNsZagD1ihVccygLr1ioLBhVQsny47uEh5swP3KScFc4JJrazx1Y7xvzmH2y5AseLgVMwomBTg2"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); ASSERT_FALSE(privateKey); } } @@ -184,20 +189,20 @@ TEST(HDWallet, privateKeyFromXPRV_InvalidVersion) { TEST(HDWallet, privateKeyFromExtended_InvalidCurve) { // invalid coin & curve, should fail const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbdeDLiSxZt88hESHUhm2AAe2EqfWM9ucdQzH3xv1HoKoLDqHMK9n"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinType(123456), DerivationPath(TWPurposeBIP44, 123456, 0, 0, 0)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(xprv, TWCoinType(123456), DerivationPath(TWPurposeBIP44, 123456, 0, 0, 0)); ASSERT_FALSE(privateKey); } TEST(HDWallet, privateKeyFromXPRV_Invalid45) { // 45th byte is not 0 const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbhw2dJ8QexahgVSfkjxU4FgmN4GLGN3Ui8oLqC6433CeyPUNVHHh"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); ASSERT_FALSE(privateKey); } TEST(HDWallet, privateKeyFromMptv) { const std::string mptv = "Mtpv7SkyM349Svcf1WiRtB5hC91ZZkVsGuv3kz1V7tThGxBFBzBLFnw6LpaSvwpHHuy8dAfMBqpBvaSAHzbffvhj2TwfojQxM7Ppm3CzW67AFL5"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(mptv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 4)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(mptv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 4)); auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); auto witness = Data{0x00, 0x14}; @@ -216,7 +221,7 @@ TEST(HDWallet, privateKeyFromMptv) { TEST(HDWallet, privateKeyFromZprv) { const std::string zprv = "zprvAdzGEQ44z4WPLNCRpDaup2RumWxLGgR8PQ9UVsSmJigXsHVDaHK1b6qGM2u9PmxB2Gx264ctAz4yRoN3Xwf1HZmKcn6vmjqwsawF4WqQjfd"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(zprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoin), 0, 0, 5)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(zprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoin), 0, 0, 5)); auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); auto address = Bitcoin::SegwitAddress(publicKey, "bc"); @@ -226,7 +231,7 @@ TEST(HDWallet, privateKeyFromZprv) { TEST(HDWallet, privateKeyFromDGRV) { const std::string dgpv = "dgpv595jAJYGBLanByCJXRzrWBZFVXdNisfuPmKRDquCQcwBbwKbeR21AtkETf4EpjBsfsK3kDZgMqhcuky1B9PrT5nxiEcjghxpUVYviHXuCmc"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(dgpv, TWCoinTypeDogecoin, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeDogecoin), 0, 0, 1)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(dgpv, TWCoinTypeDogecoin, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeDogecoin), 0, 0, 1)); auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); auto address = Bitcoin::Address(publicKey, TW::p2pkhPrefix(TWCoinTypeDogecoin)); @@ -236,7 +241,7 @@ TEST(HDWallet, privateKeyFromDGRV) { TEST(HDWallet, privateKeyFromXPRVForDGB) { const std::string xprvForDgb = "xprv9ynLofyuR3uCqCMJADwzBaPnXB53EVe5oLujvPfdvCxae3NzgEpYjZMgcUeS8EUeYfYVLG61ZgPXm9TZWiwBnLVCgd551vCwpXC19hX3mFJ"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprvForDgb, TWCoinTypeDigiByte, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeDigiByte), 0, 0, 1)); + auto privateKey = HDWallet<>::getPrivateKeyFromExtended(xprvForDgb, TWCoinTypeDigiByte, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeDigiByte), 0, 0, 1)); auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); auto address = Bitcoin::Address(publicKey, TW::p2pkhPrefix(TWCoinTypeDigiByte)); @@ -264,7 +269,7 @@ TEST(HDWallet, Bip39Vectors) { // BIP39 test vectors, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json const auto passphrase = "TREZOR"; const auto vectors = getVectors(); - for (const auto& v: vectors) { + for (const auto& v : vectors) { const std::string entropy = v[0]; const std::string mnemonic = v[1]; const std::string seed = v[2]; @@ -293,7 +298,7 @@ TEST(HDWallet, getExtendedPrivateKey) { const auto purpose = TWPurposeBIP44; const auto coin = TWCoinTypeBitcoin; const auto hdVersion = TWHDVersionZPRV; - + // default const auto extPubKey1 = wallet.getExtendedPrivateKey(purpose, coin, hdVersion); EXPECT_EQ(extPubKey1, "zprvAcwsTZNaY1f7rfwsy5GseSDStYBrxwtsBZDkb3iyuQUs4NF6n58BuH7Xj54RuaSCWtU5CiQzuYQgFgqr1HokgKcVAeGeXokhJUAJeP3VmvY"); @@ -313,7 +318,7 @@ TEST(HDWallet, getExtendedPublicKey) { const auto coin = TWCoinTypeBitcoin; const auto hdVersion = TWHDVersionZPUB; const auto derivation = TWDerivationDefault; - + // default const auto extPubKey1 = wallet.getExtendedPublicKey(purpose, coin, hdVersion); EXPECT_EQ(extPubKey1, "zpub6qwDs4uUNPDR5A2M56ot1aABSa2MNQciYn9MPS8bTk1qwAaFKcSST5S1aLidvPp9twqpaumG7vikR2vHhBXjp5oGgHyMvWK3AtUkfeEgyns"); @@ -457,6 +462,137 @@ TEST(HDWallet, HederaKey) { } } +TEST(HDWallet, FromSeedStark) { + std::string signature = "0x5a263fad6f17f23e7c7ea833d058f3656d3fe464baf13f6f5ccba9a2466ba2ce4c4a250231bcac7beb165aec4c9b049b4ba40ad8dd287dc79b92b1ffcf20cdcf1b"; + auto data = parse_hex(signature); + auto ethSignature = Ethereum::Signer::signatureDataToStructSimple(data); + auto seed = store(ethSignature.s); + ASSERT_EQ(ethSignature.s, uint256_t("34506778598894488719068064129252410649539581100963007245393949841529394744783")); + auto derivationPath = DerivationPath("m/2645'/579218131'/211006541'/1534045311'/1431804530'/1"); + auto key = HDWallet<32>::bip32DeriveRawSeed(TWCoinTypeEthereum, seed, derivationPath); + ASSERT_EQ(hex(key.bytes), "57384e99059bb1c0e51d70f0fca22d18d7191398dd39d6b9b4e0521174b2377a"); + auto addr = Ethereum::Address(key.getPublicKey(TWPublicKeyTypeSECP256k1Extended)).string(); + ASSERT_EQ(addr, "0x47bbe762944B089315ac50c9ca762F4B4884B965"); +} + +TEST(HDWallet, FromMnemonicStark) { + // https://github.com/starkware-libs/starkware-crypto-utils/blob/d3a1e655105afd66ebc07f88a179a3042407cc7b/test/js/key_derivation.spec.js#L20 + const auto mnemonic = "range mountain blast problem vibrant void vivid doctor cluster enough melody salt layer language laptop boat major space monkey unit glimpse pause change vibrant"; + const auto ethAddress = "0xA4864D977b944315389d1765Ffa7E66F74eE8cD7"; + HDWallet wallet = HDWallet(mnemonic, ""); + auto derivationPath = DerivationPath(Ethereum::accountPathFromAddress(ethAddress, "starkex", "starkdeployement", "0")); + ASSERT_EQ(derivationPath.string(), "m/2645'/579218131'/891216374'/1961790679'/2135936222'/0"); + + // ETH + { + auto ethPrivKey = wallet.getKey(TWCoinTypeEthereum, DerivationPath("m/44'/60'/0'/0/0")); + auto ethAddressFromPub = Ethereum::Address(ethPrivKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)).string(); + ASSERT_EQ(ethAddressFromPub, ethAddress); + } + + // Stark + { + auto starkPrivKey = wallet.getKeyByCurve(TWCurveStarkex, DerivationPath(derivationPath)); + auto starkPubKey = hex(starkPrivKey.getPublicKey(TWPublicKeyTypeStarkex).bytes); + ASSERT_EQ(hex(starkPrivKey.bytes), "06cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c"); + ASSERT_EQ(starkPubKey, "02d2bbdc1adaf887b0027cdde2113cfd81c60493aa6dc15d7887ddf1a82bc831"); + } +} + +TEST(HDWallet, FromMnemonicImmutableX) { + // Successfully register the user: https://api.sandbox.x.immutable.com/v1/users/0x1A817D0cC495C8157E4C734c48a1e840473CBCa1 + const auto mnemonic = "owner erupt swamp room swift final allow unaware hint identify figure cotton"; + const auto ethAddress = "0x1A817D0cC495C8157E4C734c48a1e840473CBCa1"; + HDWallet wallet = HDWallet(mnemonic, ""); + auto derivationPath = DerivationPath(Ethereum::accountPathFromAddress(ethAddress, "starkex", "immutablex", "1")); + ASSERT_EQ(derivationPath.string(), "m/2645'/579218131'/211006541'/1195162785'/289656960'/1"); + + // ETH + { + auto ethPrivKey = wallet.getKey(TWCoinTypeEthereum, DerivationPath("m/44'/60'/0'/0/0")); + ASSERT_EQ(hex(ethPrivKey.bytes), "a84f129929f6effe3fd541bcaa8a13d80714cd93c205682bea8b9e0cfc28a2ad"); + auto ethAddressFromPub = Ethereum::Address(ethPrivKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)).string(); + ASSERT_EQ(ethAddressFromPub, ethAddress); + } + + // Stark + { + auto starkPrivKey = wallet.getKeyByCurve(TWCurveStarkex, DerivationPath(derivationPath)); + auto starkPubKey = starkPrivKey.getPublicKey(TWPublicKeyTypeStarkex); + ASSERT_EQ(hex(starkPrivKey.bytes), "02d037bb9c1302295c2f9fa66bcc4ab8e353a3140600a390598777d69c1bc71a"); + ASSERT_EQ(hex(starkPubKey.bytes), "006c061ea4195769058e0e2e14cd747619a866954a412e15fa2241fdf49438cf"); + + auto starkMsg = "28419a504c5b1c83df4fdcbf7f5f36a7d5cfa8148aff2d33aed2f40a64e7ea0"; + auto starkSignature = StarkEx::MessageSigner::signMessage(starkPrivKey, starkMsg); + ASSERT_EQ(starkSignature, "077cae8f00327a2072d3ca8b31725263f61303dc0142a631561d33cb2b4cb221008d659541d59f1589b0e714ddc0a5bee77faddf093f96d529b6c55c0bffd45d"); + ASSERT_TRUE(StarkEx::MessageSigner::verifyMessage(starkPubKey, starkMsg, starkSignature)); + } +} + +TEST(HDWallet, FromMnemonicImmutableXMainnet) { + const auto mnemonic = "ocean seven canyon push fiscal banana music guess arrange edit glance school"; + const auto ethAddress = "0x39E652fE9458D391737058b0dd5eCC6ec910A7dd"; + HDWallet wallet = HDWallet(mnemonic, ""); + auto derivationPath = DerivationPath(Ethereum::accountPathFromAddress(ethAddress, "starkex", "immutablex", "1")); + ASSERT_EQ(derivationPath.string(), "m/2645'/579218131'/211006541'/1225828317'/985503965'/1"); + + // ETH + { + auto ethPrivKey = wallet.getKey(TWCoinTypeEthereum, DerivationPath("m/44'/60'/0'/0/0")); + ASSERT_EQ(hex(ethPrivKey.bytes), "3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84"); + auto ethAddressFromPub = Ethereum::Address(ethPrivKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)).string(); + ASSERT_EQ(ethAddressFromPub, ethAddress); + + std::string tosign = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3"; + auto hexEthSignature = Ethereum::MessageSigner::signMessage(ethPrivKey, tosign); + + ASSERT_EQ(hexEthSignature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); + } + + // Stark + { + auto starkPrivKey = wallet.getKeyByCurve(TWCurveStarkex, DerivationPath(derivationPath)); + auto starkPubKey = starkPrivKey.getPublicKey(TWPublicKeyTypeStarkex); + ASSERT_EQ(hex(starkPrivKey.bytes), "070128376c2cfd21e7475708049d00c83d7ab65f15368e28730bf1684dee8370"); + ASSERT_EQ(hex(starkPubKey.bytes), "00453ca02b347f80e5ddfc4caf254852fc05b172b37bca8f7e28600631d12dfe"); + + auto starkMsg = "76b66c453cd1b812032ff206a28df59f6abe41e805b9f1c48a1c4afe780756c"; + auto starkSignature = StarkEx::MessageSigner::signMessage(starkPrivKey, starkMsg); + ASSERT_EQ(starkSignature, "070ad88f79650fbdc152affd738d4ec29888bed554ea74f9ad8ca7031ef300b50597f4a62752336db06e6d37dfc18047fdd40804f5fd19cebfda8cac91e4f178"); + ASSERT_TRUE(StarkEx::MessageSigner::verifyMessage(starkPubKey, starkMsg, starkSignature)); + } +} + +TEST(HDWallet, FromMnemonicImmutableXMainnetFromSignature) { + // Successfully register: https://api.x.immutable.com/v1/users/0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37 + const auto mnemonic = "obscure opera favorite shuffle mail tip age debate dirt pact cement loyal"; + const auto ethAddress = "0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37"; + HDWallet wallet = HDWallet(mnemonic, ""); + auto derivationPath = DerivationPath(Ethereum::accountPathFromAddress(ethAddress, "starkex", "immutablex", "1")); + ASSERT_EQ(derivationPath.string(), "m/2645'/579218131'/211006541'/2124474935'/1609799702'/1"); + + // ETH + stark + { + auto ethPrivKey = wallet.getKey(TWCoinTypeEthereum, DerivationPath("m/44'/60'/0'/0/0")); + ASSERT_EQ(hex(ethPrivKey.bytes), "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"); + auto ethAddressFromPub = Ethereum::Address(ethPrivKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)).string(); + ASSERT_EQ(ethAddressFromPub, ethAddress); + auto signature = Ethereum::MessageSigner::signMessage(ethPrivKey, "Only sign this request if you’ve initiated an action with Immutable X."); + ASSERT_EQ(signature, "18b1be8b78807d3326e28bc286d7ee3d068dcd90b1949ce1d25c1f99825f26e70992c5eb7f44f76b202aceded00d74f771ed751f2fe538eec01e338164914fe001"); + auto starkPrivKey = ImmutableX::getPrivateKeyFromRawSignature(parse_hex(signature), DerivationPath(derivationPath)); + auto starkPubKey = starkPrivKey.getPublicKey(TWPublicKeyTypeStarkex); + ASSERT_EQ(hex(starkPrivKey.bytes), "04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de"); + ASSERT_EQ(hex(starkPubKey.bytes), "00e5b9b11f8372610ef35d647a1dcaba1a4010716588d591189b27bf3c2d5095"); + auto signatureToSend = Ethereum::MessageSigner::signMessage(ethPrivKey, "Only sign this key linking request from Immutable X"); + ASSERT_EQ(signatureToSend, "646da4160f7fc9205e6f502fb7691a0bf63ecbb74bbb653465cd62388dd9f56325ab1e4a9aba99b1661e3e6251b42822855a71e60017b310b9f90e990a12e1dc01"); + + auto starkMsg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf"; + auto starkSignature = StarkEx::MessageSigner::signMessage(starkPrivKey, starkMsg); + ASSERT_EQ(starkSignature, "04cf5f21333dd189ada3c0f2a51430d733501a9b1d5e07905273c1938cfb261e05b6013d74adde403e8953743a338c8d414bb96bf69d2ca1a91a85ed2700a528"); + ASSERT_TRUE(StarkEx::MessageSigner::verifyMessage(starkPubKey, starkMsg, starkSignature)); + } +} + TEST(HDWallet, NearKey) { const auto derivPath = "m/44'/397'/0'"; HDWallet wallet = HDWallet("owner erupt swamp room swift final allow unaware hint identify figure cotton", ""); @@ -469,4 +605,4 @@ TEST(HDWallet, NearKey) { } } -} // namespace +} // namespace TW::HDWalletTests diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index d34edd03468..9cbf538898b 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -17,6 +17,10 @@ #include #include #include +#include +#include +#include +#include #include #include "HexCoding.h" @@ -498,6 +502,47 @@ TEST(HDWallet, GetKeyByCurve) { assertHexEqual(privateKeyData2, "a13df52d5a5b438bbf921bbf86276e4347fe8e2f2ed74feaaee12b77d6d26f86"); } +TEST(TWHDWallet, FromMnemonicImmutableXMainnetFromSignature) { + // Successfully register: https://api.x.immutable.com/v1/users/0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37 + const auto mnemonic = STRING("obscure opera favorite shuffle mail tip age debate dirt pact cement loyal"); + const auto ethAddress = STRING("0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37"); + const auto layer = STRING("starkex"); + const auto application = STRING("immutablex"); + const auto index = STRING("1"); + const auto ethDerivationPath = STRING("m/44'/60'/0'/0/0"); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(mnemonic.get(), STRING("").get())); + auto derivationPath = WRAPS(TWEthereumEip2645GetPath(ethAddress.get(), layer.get(), application.get(), index.get())); + assertStringsEqual(derivationPath, "m/2645'/579218131'/211006541'/2124474935'/1609799702'/1"); + + // Retrieve eth private key + auto ethPrivateKey = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), TWCoinTypeEthereum, ethDerivationPath.get())); + const auto ethPrivateKeyData = WRAPD(TWPrivateKeyData(ethPrivateKey.get())); + assertHexEqual(ethPrivateKeyData, "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"); + + // StarkKey Derivation Path + const auto starkDerivationPath = WRAP(TWDerivationPath, TWDerivationPathCreateWithString(derivationPath.get())); + + // Retrieve Stark Private key part + const auto ethMsg = STRING("Only sign this request if you’ve initiated an action with Immutable X."); + const auto ethSignature = WRAPS(TWEthereumMessageSignerSignMessage(ethPrivateKey.get(), ethMsg.get())); + assertStringsEqual(ethSignature, "18b1be8b78807d3326e28bc286d7ee3d068dcd90b1949ce1d25c1f99825f26e70992c5eb7f44f76b202aceded00d74f771ed751f2fe538eec01e338164914fe001"); + const auto starkPrivateKey = WRAP(TWPrivateKey, TWStarkWareGetStarkKeyFromSignature(starkDerivationPath.get(), ethSignature.get())); + const auto starkPrivateKeyData = WRAPD(TWPrivateKeyData(starkPrivateKey.get())); + const auto starkPubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyByType(starkPrivateKey.get(), TWPublicKeyTypeStarkex)); + const auto starkPublicKeyData = WRAPD(TWPublicKeyData(starkPubKey.get())); + assertHexEqual(starkPrivateKeyData, "04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de"); + assertHexEqual(starkPublicKeyData, "00e5b9b11f8372610ef35d647a1dcaba1a4010716588d591189b27bf3c2d5095"); + + // Account register + const auto ethMsgToRegister = STRING("Only sign this key linking request from Immutable X"); + const auto ethSignatureToRegister = WRAPS(TWEthereumMessageSignerSignMessage(ethPrivateKey.get(), ethMsgToRegister.get())); + assertStringsEqual(ethSignatureToRegister, "646da4160f7fc9205e6f502fb7691a0bf63ecbb74bbb653465cd62388dd9f56325ab1e4a9aba99b1661e3e6251b42822855a71e60017b310b9f90e990a12e1dc01"); + const auto starkMsg = STRING("463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf"); + const auto starkSignature = WRAPS(TWStarkExMessageSignerSignMessage(starkPrivateKey.get(), starkMsg.get())); + assertStringsEqual(starkSignature, "04cf5f21333dd189ada3c0f2a51430d733501a9b1d5e07905273c1938cfb261e05b6013d74adde403e8953743a338c8d414bb96bf69d2ca1a91a85ed2700a528"); + ASSERT_TRUE(TWStarkExMessageSignerVerifyMessage(starkPubKey.get(), starkMsg.get(), starkSignature.get())); +} + TEST(TWHDWallet, Derive_XpubPub_vs_PrivPub) { // Test different routes for deriving address from mnemonic, result should be the same: // - Direct: mnemonic -> seed -> privateKey -> publicKey -> address diff --git a/walletconsole/lib/Address.cpp b/walletconsole/lib/Address.cpp index 7a082f7413c..6aab3c60ca4 100644 --- a/walletconsole/lib/Address.cpp +++ b/walletconsole/lib/Address.cpp @@ -29,7 +29,7 @@ bool Address::addrPub(const string& coinid, const string& pubkey_in, string& res pubDat = parse_hex(pubkey_in); } catch (exception& ex) { _out << "Error: could not parse public key data" << endl; - return false; + return false; } auto ctype = (TWCoinType)coin.c; PublicKey pubKey = PublicKey(pubDat, (TWPublicKeyType)coin.pubKeyType); @@ -45,7 +45,7 @@ bool Address::addrPri(const string& coinid, const string& prikey_in, string& res priDat = parse_hex(prikey_in); } catch (exception& ex) { _out << "Error: could not parse private key data" << endl; - return false; + return false; } auto ctype = (TWCoinType)coin.c; PrivateKey priKey = PrivateKey(priDat); @@ -108,7 +108,7 @@ bool Address::deriveFromXpubIndex(const string& coinid, const string& xpub, cons dp.setChange(0); dp.setAddress(index); - const auto publicKey = HDWallet::getPublicKeyFromExtended(xpub, ctype, dp); + const auto publicKey = HDWallet<>::getPublicKeyFromExtended(xpub, ctype, dp); if (!publicKey) { return false; } res = TW::deriveAddress(ctype, publicKey.value()); return true; From 9e00215b287a4b2e8de0cf31a3ff4eb25fef9179 Mon Sep 17 00:00:00 2001 From: Joao Cordeiro Date: Fri, 23 Dec 2022 11:05:05 +0000 Subject: [PATCH 066/426] Updated docker build (#2809) * Update libssl version to 1.1.1 for docker build * Install rust during docker build * Switch curl to wget to keep consistency with rest of Dockerfile * Install cbindgen on docker build * Add missing include --- Dockerfile | 9 ++++++++- src/Ethereum/EIP2645.cpp | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fae3ef89269..ee2bff16772 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc | apt-key && apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' -RUN wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb && dpkg -i ./libssl1.1_1.1.0g-2ubuntu4_amd64.deb +RUN wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.16_amd64.deb && dpkg -i ./libssl1.1_1.1.1f-1ubuntu2.16_amd64.deb # Install required packages for dev RUN apt-get update \ && apt-get install -y \ @@ -39,6 +39,13 @@ RUN apt-get update \ ENV CC=/usr/bin/clang-14 ENV CXX=/usr/bin/clang++-14 +# Install rust +RUN wget "https://sh.rustup.rs" -O rustup.sh \ + && sh rustup.sh -y +ENV PATH="/root/.cargo/bin:${PATH}" +RUN cargo install --force cbindgen \ + && rustup target add wasm32-unknown-emscripten + # ↑ Setup build environment # ↓ Build and compile wallet core diff --git a/src/Ethereum/EIP2645.cpp b/src/Ethereum/EIP2645.cpp index 9a601643999..c5e1bc72f93 100644 --- a/src/Ethereum/EIP2645.cpp +++ b/src/Ethereum/EIP2645.cpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace TW::Ethereum::internal { From 3ef2428a9841301c1296bde104eb2fe4d96a5035 Mon Sep 17 00:00:00 2001 From: Peng Huang Date: Fri, 30 Dec 2022 04:55:51 -0500 Subject: [PATCH 067/426] Change several classes to structs (#2826) --- include/TrustWalletCore/TWBitcoinMessageSigner.h | 2 +- include/TrustWalletCore/TWEthereumEip2645.h | 2 +- include/TrustWalletCore/TWEthereumMessageSigner.h | 2 +- include/TrustWalletCore/TWStarkExMessageSigner.h | 2 +- include/TrustWalletCore/TWStarkWare.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/TrustWalletCore/TWBitcoinMessageSigner.h b/include/TrustWalletCore/TWBitcoinMessageSigner.h index d0fcd2e17bd..8cfe84f14da 100644 --- a/include/TrustWalletCore/TWBitcoinMessageSigner.h +++ b/include/TrustWalletCore/TWBitcoinMessageSigner.h @@ -18,7 +18,7 @@ TW_EXTERN_C_BEGIN /// Bitcoin Core and some other wallets support a message signing & verification format, to create a proof (a signature) /// that someone has access to the private keys of a specific address. /// This feature currently works on old legacy addresses only. -TW_EXPORT_CLASS +TW_EXPORT_STRUCT struct TWBitcoinMessageSigner; /// Sign a message. diff --git a/include/TrustWalletCore/TWEthereumEip2645.h b/include/TrustWalletCore/TWEthereumEip2645.h index f6315908c33..95678fa8be6 100644 --- a/include/TrustWalletCore/TWEthereumEip2645.h +++ b/include/TrustWalletCore/TWEthereumEip2645.h @@ -11,7 +11,7 @@ TW_EXTERN_C_BEGIN -TW_EXPORT_CLASS +TW_EXPORT_STRUCT struct TWEthereumEip2645; /// Generate a layer 2 eip2645 derivation path from eth address, layer, application and given index. diff --git a/include/TrustWalletCore/TWEthereumMessageSigner.h b/include/TrustWalletCore/TWEthereumMessageSigner.h index 1fb230b143f..e33a3e3dad1 100644 --- a/include/TrustWalletCore/TWEthereumMessageSigner.h +++ b/include/TrustWalletCore/TWEthereumMessageSigner.h @@ -18,7 +18,7 @@ TW_EXTERN_C_BEGIN /// /// Ethereum and some other wallets support a message signing & verification format, to create a proof (a signature) /// that someone has access to the private keys of a specific address. -TW_EXPORT_CLASS +TW_EXPORT_STRUCT struct TWEthereumMessageSigner; /// Sign a message. diff --git a/include/TrustWalletCore/TWStarkExMessageSigner.h b/include/TrustWalletCore/TWStarkExMessageSigner.h index a7569171c39..ea012369795 100644 --- a/include/TrustWalletCore/TWStarkExMessageSigner.h +++ b/include/TrustWalletCore/TWStarkExMessageSigner.h @@ -17,7 +17,7 @@ TW_EXTERN_C_BEGIN /// /// StarkEx and some other wallets support a message signing & verification format, to create a proof (a signature) /// that someone has access to the private keys of a specific address. -TW_EXPORT_CLASS +TW_EXPORT_STRUCT struct TWStarkExMessageSigner; /// Sign a message. diff --git a/include/TrustWalletCore/TWStarkWare.h b/include/TrustWalletCore/TWStarkWare.h index 0990d67be86..593876c16f3 100644 --- a/include/TrustWalletCore/TWStarkWare.h +++ b/include/TrustWalletCore/TWStarkWare.h @@ -13,7 +13,7 @@ TW_EXTERN_C_BEGIN -TW_EXPORT_CLASS +TW_EXPORT_STRUCT struct TWStarkWare; /// Generates the private stark key at the given derivation path from a valid eth signature From 42408e5d394617e278e597950b18a4173a9a620c Mon Sep 17 00:00:00 2001 From: Ayush Bherwani Date: Fri, 6 Jan 2023 14:16:48 +0530 Subject: [PATCH 068/426] Agoric Support (#2824) --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../blockchains/agoric/TestAgoricAddress.kt | 32 +++++++++ .../blockchains/agoric/TestAgoricSigner.kt | 68 +++++++++++++++++++ docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 31 +++++++++ swift/Tests/Blockchains/AgoricTests.swift | 63 +++++++++++++++++ swift/Tests/CoinAddressDerivationTests.swift | 4 +- tests/chains/Agoric/AddressTests.cpp | 43 ++++++++++++ tests/chains/Agoric/TWCoinTypeTests.cpp | 37 ++++++++++ tests/common/CoinAddressDerivationTests.cpp | 3 + 11 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricSigner.kt create mode 100644 swift/Tests/Blockchains/AgoricTests.swift create mode 100644 tests/chains/Agoric/AddressTests.cpp create mode 100644 tests/chains/Agoric/TWCoinTypeTests.cpp 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 8d60a752d44..b3384921873 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 @@ -108,5 +108,6 @@ class CoinAddressDerivationTests { APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) HEDERA -> assertEquals("0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5", address) SECRET -> assertEquals("secret1f69sk5033zcdr2p2yf3xjehn7xvgdeq09d2llh", address) + AGORIC -> assertEquals("agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricAddress.kt new file mode 100644 index 00000000000..9cabca65f15 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricAddress.kt @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 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.agoric + +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 TestAgoricAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + + val key = PrivateKey("037048190544fa57651452f477c096de4f3073e7835cf3845b04602563a73f73".toHexByteArray()) + val pubkey = key.getPublicKeySecp256k1(true) + val address = AnyAddress(pubkey, CoinType.AGORIC) + val expected = AnyAddress("agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5", CoinType.AGORIC) + + assertEquals(pubkey.data().toHex(), "0x03df9a5e4089f89d45913fb2b856de984c7e8bf1344cc6444cc9705899a48c939d") + assertEquals(address.description(), expected.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricSigner.kt new file mode 100644 index 00000000000..7567a61f21f --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricSigner.kt @@ -0,0 +1,68 @@ +// Copyright © 2017-2022 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.agoric + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.AnyAddress +import wallet.core.jni.CoinType +import wallet.core.jni.PrivateKey +import wallet.core.jni.proto.Cosmos + +class TestAgoricSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun AgoricTransactionSigning() { + val key = PrivateKey("037048190544fa57651452f477c096de4f3073e7835cf3845b04602563a73f73".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(true) + val from = AnyAddress(publicKey, CoinType.AGORIC).description() + + val transferAmount = Cosmos.Amount.newBuilder().apply { + amount = "1" + denom = "ubld" + }.build() + + val message = Cosmos.Message.newBuilder().apply { + sendCoinsMessage = Cosmos.Message.Send.newBuilder().apply { + fromAddress = from + toAddress = "agoric1cqvwa8jr6pmt45jndanc8lqmdsxjkhw0yertc0" + addAllAmounts(listOf(transferAmount)) + }.build() + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "2000" + denom = "ubld" + }.build() + + val cosmosFee = Cosmos.Fee.newBuilder().apply { + gas = 100000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = Cosmos.SigningMode.Protobuf + accountNumber = 62972 + chainId = "agoric-3" + sequence = 1 + fee = cosmosFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, CoinType.AGORIC, Cosmos.SigningOutput.parser()) + + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWFnb3JpYzE4enZ2Z2s2ajNlcTV3ZDdtcXhjY2d0MjBnejJ3OTRjeTg4YWVrNRItYWdvcmljMWNxdndhOGpyNnBtdDQ1am5kYW5jOGxxbWRzeGpraHcweWVydGMwGgkKBHVibGQSATESZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA9+aXkCJ+J1FkT+yuFbemEx+i/E0TMZETMlwWJmkjJOdEgQKAggBGAESEgoMCgR1YmxkEgQyMDAwEKCNBhpAenbGO4UBK610dwSY6l5pl58qwHW1OujQ/9vF9unQdrA1SE0b/2mZxnevy5y3u6pJfBffWUfCx68PcVEu7D3EYQ==\"}") + } +} diff --git a/docs/registry.md b/docs/registry.md index b5ca89f1dbe..0d9e425c1d1 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -51,6 +51,7 @@ This list is generated from [./registry.json](../registry.json) | 501 | Solana | SOL | | | | 508 | Elrond | eGLD | | | | 529 | Secret | SCRT | | | +| 564 | Agoric | BLD | | | | 637 | Aptos | APT | | | | 714 | BNB Beacon Chain | BNB | | | | 818 | VeChain | VET | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index aaf37364468..cb196d5920b 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -126,6 +126,7 @@ enum TWCoinType { TWCoinTypeAptos = 637, TWCoinTypeHedera = 3030, TWCoinTypeSecret = 529, + TWCoinTypeAgoric = 564, }; /// Returns the blockchain for a coin type. diff --git a/registry.json b/registry.json index d2279f7144a..fabf45f3b5b 100644 --- a/registry.json +++ b/registry.json @@ -2880,5 +2880,36 @@ "source": "https://github.com/hashgraph", "documentation": "https://docs.hedera.com" } + }, + { + "id": "agoric", + "name": "Agoric", + "coinId": 564, + "symbol": "BLD", + "decimals": 6, + "blockchain": "Cosmos", + "derivation": [ + { + "path": "m/44'/564'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "hrp": "agoric", + "chainId": "agoric-3", + "addressHasher": "sha256ripemd", + "explorer": { + "url": "https://atomscan.com/agoric", + "txPath": "/transactions/", + "accountPath": "/accounts/", + "sampleTx": "576224B1A0F3D56BA23C5350C2A0E3CEA86F40005B828793E5ACBC2F4813152E", + "sampleAccount": "agoric1cqvwa8jr6pmt45jndanc8lqmdsxjkhw0yertc0" + }, + "info": { + "url": "https://agoric.com", + "source": "https://github.com/Agoric/agoric-sdk", + "rpc": "https://agoric-rpc.polkachu.com", + "documentation": "https://docs.agoric.com" + } } ] diff --git a/swift/Tests/Blockchains/AgoricTests.swift b/swift/Tests/Blockchains/AgoricTests.swift new file mode 100644 index 00000000000..19404b3447b --- /dev/null +++ b/swift/Tests/Blockchains/AgoricTests.swift @@ -0,0 +1,63 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class AgoricTests: XCTestCase { + + func testAddress() { + let key = PrivateKey(data: Data(hexString: "037048190544fa57651452f477c096de4f3073e7835cf3845b04602563a73f73")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: true) + let address = AnyAddress(publicKey: pubkey, coin: .agoric) + let addressFromString = AnyAddress(string: "agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5", coin: .agoric)! + + XCTAssertEqual(pubkey.data.hexString, "03df9a5e4089f89d45913fb2b856de984c7e8bf1344cc6444cc9705899a48c939d") + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSign() { + let privateKey = PrivateKey(data: Data(hexString: "037048190544fa57651452f477c096de4f3073e7835cf3845b04602563a73f73")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .agoric) + + let sendCoinsMessage = CosmosMessage.Send.with { + $0.fromAddress = fromAddress.description + $0.toAddress = "agoric1cqvwa8jr6pmt45jndanc8lqmdsxjkhw0yertc0" + $0.amounts = [CosmosAmount.with { + $0.amount = "1" + $0.denom = "ubld" + }] + } + + + let fee = CosmosFee.with { + $0.gas = 100000 + $0.amounts = [CosmosAmount.with { + $0.amount = "2000" + $0.denom = "ubld" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 62972 + $0.chainID = "agoric-3" + $0.sequence = 1 + $0.messages = [ + CosmosMessage.with { + $0.sendCoinsMessage = sendCoinsMessage + } + ] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .agoric) + + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWFnb3JpYzE4enZ2Z2s2ajNlcTV3ZDdtcXhjY2d0MjBnejJ3OTRjeTg4YWVrNRItYWdvcmljMWNxdndhOGpyNnBtdDQ1am5kYW5jOGxxbWRzeGpraHcweWVydGMwGgkKBHVibGQSATESZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA9+aXkCJ+J1FkT+yuFbemEx+i/E0TMZETMlwWJmkjJOdEgQKAggBGAESEgoMCgR1YmxkEgQyMDAwEKCNBhpAenbGO4UBK610dwSY6l5pl58qwHW1OujQ/9vF9unQdrA1SE0b/2mZxnevy5y3u6pJfBffWUfCx68PcVEu7D3EYQ==\"}") + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 081a61cae55..d839b605452 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -262,7 +262,9 @@ class CoinAddressDerivationTests: XCTestCase { case .secret: let expectedResult = "secret1f69sk5033zcdr2p2yf3xjehn7xvgdeq09d2llh" assertCoinDerivation(coin, expectedResult, derivedAddress, address) - + case .agoric: + let expectedResult = "agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() } diff --git a/tests/chains/Agoric/AddressTests.cpp b/tests/chains/Agoric/AddressTests.cpp new file mode 100644 index 00000000000..4e18f242374 --- /dev/null +++ b/tests/chains/Agoric/AddressTests.cpp @@ -0,0 +1,43 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "Cosmos/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include +#include + +namespace TW::Cosmos::tests { + +TEST(AgoricAddress, Valid) { + ASSERT_TRUE(Address::isValid(TWCoinTypeSecret, "secret16vw3fp7x35tzmwlkdkyzr8vgscn0zewtduyjuf")); + ASSERT_TRUE(Address::isValid(TWCoinTypeSecret, "secret15rgv8teecnt53h0gdvngzt3am3yuz3rxh4fnle")); +} + +TEST(AgoricAddress, Invalid) { + ASSERT_FALSE(Address::isValid(TWCoinTypeAgoric, "agoric1cqvwa8jr6pmt45jndanc8lqmdsxjkhw0yert212")); + ASSERT_FALSE(Address::isValid(TWCoinTypeAgoric, "osmo1nvt7lu3e0l8wqahlya6m98zmma6qc9r40evp0v")); +} + +TEST(AgoricAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("037048190544fa57651452f477c096de4f3073e7835cf3845b04602563a73f73")); + auto address = Address(TWCoinTypeAgoric, privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + ASSERT_EQ(address.string(), "agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5"); +} + +TEST(AgoricAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("03df9a5e4089f89d45913fb2b856de984c7e8bf1344cc6444cc9705899a48c939d"), TWPublicKeyTypeSECP256k1); + auto address = Address(TWCoinTypeAgoric, publicKey); + ASSERT_EQ(address.string(), "agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5"); +} + +TEST(AgoricAddress, FromString) { + Address address; + EXPECT_TRUE(Address::decode("agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5", address)); + EXPECT_EQ(address.string(), "agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5"); +} +} diff --git a/tests/chains/Agoric/TWCoinTypeTests.cpp b/tests/chains/Agoric/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..d113c5e1ef3 --- /dev/null +++ b/tests/chains/Agoric/TWCoinTypeTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 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. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWAgoricCoinType, TWCoinType) { + const auto coin = TWCoinTypeAgoric; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("576224B1A0F3D56BA23C5350C2A0E3CEA86F40005B828793E5ACBC2F4813152E")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("agoric1cqvwa8jr6pmt45jndanc8lqmdsxjkhw0yertc0")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "agoric"); + assertStringsEqual(name, "Agoric"); + assertStringsEqual(symbol, "BLD"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 6); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "agoric-3"); + assertStringsEqual(txUrl, "https://atomscan.com/agoric/transactions/576224B1A0F3D56BA23C5350C2A0E3CEA86F40005B828793E5ACBC2F4813152E"); + assertStringsEqual(accUrl, "https://atomscan.com/agoric/accounts/agoric1cqvwa8jr6pmt45jndanc8lqmdsxjkhw0yertc0"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index be0e69bce0a..64d792feea9 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -258,6 +258,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeSecret: EXPECT_EQ(address, "secret1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0m7t23a"); break; + case TWCoinTypeAgoric: + EXPECT_EQ(address, "agoric1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0txauuh"); + break; // no default branch here, intentionally, to better notice any missing coins } } From a5563f8dc6d50d9ebd4e9767f09617533c7b9742 Mon Sep 17 00:00:00 2001 From: Adam V <13562139+catenocrypt@users.noreply.github.com> Date: Mon, 9 Jan 2023 10:01:22 +0200 Subject: [PATCH 069/426] Add extra hrp and coin check to lockScriptForAddress() (#2814) --- src/Bitcoin/Script.cpp | 119 +++++++++++------- .../GroestlcoinTransactionSignerTests.swift | 3 + tests/chains/Bitcoin/TWBitcoinScriptTests.cpp | 4 +- tests/chains/DigiByte/TWDigiByteTests.cpp | 2 + tests/chains/ECash/TWECashTests.cpp | 1 + .../Groestlcoin/TWGroestlcoinSigningTests.cpp | 4 + 6 files changed, 89 insertions(+), 44 deletions(-) diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index fcd8f383268..407a8e5152e 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -9,6 +9,7 @@ #include "OpCodes.h" #include "Script.h" #include "SegwitAddress.h" +#include #include "../BinaryCoding.h" #include "../Coin.h" @@ -291,6 +292,7 @@ void Script::encode(Data& data) const { } Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType coin) { + // First try legacy address, for all coins if (Address::isValid(string)) { auto address = Address(string); auto p2pkh = TW::p2pkhPrefix(coin); @@ -301,16 +303,23 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c data.reserve(Address::size - 1); std::copy(address.bytes.begin() + 1, address.bytes.end(), std::back_inserter(data)); return buildPayToPublicKeyHash(data); - } else if (p2sh == address.bytes[0]) { + } + if (p2sh == address.bytes[0]) { // address starts with 3/M auto data = Data(); data.reserve(Address::size - 1); std::copy(address.bytes.begin() + 1, address.bytes.end(), std::back_inserter(data)); return buildPayToScriptHash(data); } - } else if (SegwitAddress::isValid(string)) { - const auto result = SegwitAddress::decode(string); - // address starts with bc/ltc + return {}; + } + + // Second, try Segwit address, for all coins; also check HRP + if (const auto result = SegwitAddress::decode(string); + std::get<2>(result) && + (std::get<1>(result) == stringForHRP(TW::hrp(coin)) || (coin == TWCoinTypeBitcoin && std::get<1>(result) == SegwitAddress::TestnetPrefix)) + ) { + // address starts with bc/ltc/... const auto address = std::get<0>(result); if (address.witnessVersion == 0) { return buildPayToV0WitnessProgram(address.witnessProgram); @@ -318,45 +327,71 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c if (address.witnessVersion == 1 && address.witnessProgram.size() == 32) { return buildPayToV1WitnessProgram(address.witnessProgram); } - } else if (BitcoinCashAddress::isValid(string)) { - auto address = BitcoinCashAddress(string); - auto bitcoinAddress = address.legacyAddress(); - return lockScriptForAddress(bitcoinAddress.string(), TWCoinTypeBitcoinCash); - } else if (Decred::Address::isValid(string)) { - auto bytes = Base58::bitcoin.decodeCheck(string, Hash::HasherBlake256d); - if (bytes[1] == TW::p2pkhPrefix(TWCoinTypeDecred)) { - return buildPayToPublicKeyHash(Data(bytes.begin() + 2, bytes.end())); - } - if (bytes[1] == TW::p2shPrefix(TWCoinTypeDecred)) { - return buildPayToScriptHash(Data(bytes.begin() + 2, bytes.end())); - } - } else if (ECashAddress::isValid(string)) { - auto address = ECashAddress(string); - auto bitcoinAddress = address.legacyAddress(); - return lockScriptForAddress(bitcoinAddress.string(), TWCoinTypeECash); - } else if (Groestlcoin::Address::isValid(string)) { - auto address = Groestlcoin::Address(string); - auto data = Data(); - data.reserve(Address::size - 1); - std::copy(address.bytes.begin() + 1, address.bytes.end(), std::back_inserter(data)); - if (address.bytes[0] == TW::p2pkhPrefix(TWCoinTypeGroestlcoin)) { - return buildPayToPublicKeyHash(data); - } - if (address.bytes[0] == TW::p2shPrefix(TWCoinTypeGroestlcoin)) { - return buildPayToScriptHash(data); - } - } else if (Zcash::TAddress::isValid(string)) { - auto address = Zcash::TAddress(string); - auto data = Data(); - data.reserve(Address::size - 2); - std::copy(address.bytes.begin() + 2, address.bytes.end(), std::back_inserter(data)); - if (address.bytes[1] == TW::p2pkhPrefix(TWCoinTypeZcash)) { - return buildPayToPublicKeyHash(data); - } else if (address.bytes[1] == TW::p2shPrefix(TWCoinTypeZcash)) { - return buildPayToScriptHash(data); - } + return {}; + } + + // Thirdly, coin-specific address formats + switch (coin) { + case TWCoinTypeBitcoinCash: + if (BitcoinCashAddress::isValid(string)) { + auto address = BitcoinCashAddress(string); + auto bitcoinAddress = address.legacyAddress(); + return lockScriptForAddress(bitcoinAddress.string(), TWCoinTypeBitcoinCash); + } + return {}; + + case TWCoinTypeDecred: + if (Decred::Address::isValid(string)) { + auto bytes = Base58::bitcoin.decodeCheck(string, Hash::HasherBlake256d); + if (bytes[1] == TW::p2pkhPrefix(TWCoinTypeDecred)) { + return buildPayToPublicKeyHash(Data(bytes.begin() + 2, bytes.end())); + } + if (bytes[1] == TW::p2shPrefix(TWCoinTypeDecred)) { + return buildPayToScriptHash(Data(bytes.begin() + 2, bytes.end())); + } + } + return {}; + + case TWCoinTypeECash: + if (ECashAddress::isValid(string)) { + auto address = ECashAddress(string); + auto bitcoinAddress = address.legacyAddress(); + return lockScriptForAddress(bitcoinAddress.string(), TWCoinTypeECash); + } + return {}; + + case TWCoinTypeGroestlcoin: + if (Groestlcoin::Address::isValid(string)) { + auto address = Groestlcoin::Address(string); + auto data = Data(); + data.reserve(Address::size - 1); + std::copy(address.bytes.begin() + 1, address.bytes.end(), std::back_inserter(data)); + if (address.bytes[0] == TW::p2pkhPrefix(TWCoinTypeGroestlcoin)) { + return buildPayToPublicKeyHash(data); + } + if (address.bytes[0] == TW::p2shPrefix(TWCoinTypeGroestlcoin)) { + return buildPayToScriptHash(data); + } + } + return {}; + + case TWCoinTypeZcash: + if (Zcash::TAddress::isValid(string)) { + auto address = Zcash::TAddress(string); + auto data = Data(); + data.reserve(Address::size - 2); + std::copy(address.bytes.begin() + 2, address.bytes.end(), std::back_inserter(data)); + if (address.bytes[1] == TW::p2pkhPrefix(TWCoinTypeZcash)) { + return buildPayToPublicKeyHash(data); + } else if (address.bytes[1] == TW::p2shPrefix(TWCoinTypeZcash)) { + return buildPayToScriptHash(data); + } + } + return {}; + + default: + return {}; } - return {}; } } // namespace TW::Bitcoin diff --git a/swift/Tests/Blockchains/GroestlcoinTransactionSignerTests.swift b/swift/Tests/Blockchains/GroestlcoinTransactionSignerTests.swift index 09bdb7119ee..58ab98538a9 100644 --- a/swift/Tests/Blockchains/GroestlcoinTransactionSignerTests.swift +++ b/swift/Tests/Blockchains/GroestlcoinTransactionSignerTests.swift @@ -14,6 +14,7 @@ class GroestlcoinTransactionSignerTests: XCTestCase { func testSignP2WPKH() throws { var input = BitcoinSigningInput.with { + $0.coinType = CoinType.groestlcoin.rawValue $0.hashType = BitcoinScript.hashTypeForCoin(coinType: .groestlcoin) $0.amount = 2500 $0.byteFee = 1 @@ -67,6 +68,7 @@ class GroestlcoinTransactionSignerTests: XCTestCase { func testSignP2PKH() throws { var input = BitcoinSigningInput.with { + $0.coinType = CoinType.groestlcoin.rawValue $0.hashType = BitcoinScript.hashTypeForCoin(coinType: .groestlcoin) $0.amount = 2500 $0.byteFee = 1 @@ -118,6 +120,7 @@ class GroestlcoinTransactionSignerTests: XCTestCase { func testSignP2SH_P2WPKH() throws { var input = BitcoinSigningInput.with { + $0.coinType = CoinType.groestlcoin.rawValue $0.hashType = BitcoinScript.hashTypeForCoin(coinType: .groestlcoin) $0.amount = 5000 $0.byteFee = 1 diff --git a/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp b/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp index 2f9a3e1ad29..9d1aa7f4ce0 100644 --- a/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp @@ -210,11 +210,11 @@ TEST(TWBitcoinScript, LockScriptForP2WSHAddress) { } TEST(TWBitcoinScript, LockScriptForCashAddress) { - auto script = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("bitcoincash:pzclklsyx9f068hd00a0vene45akeyrg7vv0053uqf").get(), TWCoinTypeBitcoin)); + auto script = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("bitcoincash:pzclklsyx9f068hd00a0vene45akeyrg7vv0053uqf").get(), TWCoinTypeBitcoinCash)); auto scriptData = WRAPD(TWBitcoinScriptData(script.get())); assertHexEqual(scriptData, "a914b1fb7e043152fd1eed7bfaf66679ad3b6c9068f387"); - auto script2 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("bitcoincash:qpk05r5kcd8uuzwqunn8rlx5xvuvzjqju5rch3tc0u").get(), TWCoinTypeBitcoin)); + auto script2 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("bitcoincash:qpk05r5kcd8uuzwqunn8rlx5xvuvzjqju5rch3tc0u").get(), TWCoinTypeBitcoinCash)); auto scriptData2 = WRAPD(TWBitcoinScriptData(script2.get())); assertHexEqual(scriptData2, "76a9146cfa0e96c34fce09c0e4e671fcd43338c14812e588ac"); } diff --git a/tests/chains/DigiByte/TWDigiByteTests.cpp b/tests/chains/DigiByte/TWDigiByteTests.cpp index c824018607c..1c40d8815a9 100644 --- a/tests/chains/DigiByte/TWDigiByteTests.cpp +++ b/tests/chains/DigiByte/TWDigiByteTests.cpp @@ -32,6 +32,7 @@ TEST(DigiByteTransaction, SignTransaction) { const int64_t fee = 1000; auto input = Bitcoin::Proto::SigningInput(); + input.set_coin_type(TWCoinTypeDigiByte); input.set_hash_type(TWBitcoinSigHashTypeAll); input.set_amount(amount); input.set_byte_fee(1); @@ -98,6 +99,7 @@ TEST(DigiByteTransaction, SignP2WPKH) { const int64_t amount = 2000000; Proto::SigningInput input; + input.set_coin_type(TWCoinTypeDigiByte); input.set_hash_type(TWBitcoinSigHashTypeAll); input.set_amount(amount); input.set_byte_fee(1); diff --git a/tests/chains/ECash/TWECashTests.cpp b/tests/chains/ECash/TWECashTests.cpp index f258e4a4816..bd9f5738b3b 100644 --- a/tests/chains/ECash/TWECashTests.cpp +++ b/tests/chains/ECash/TWECashTests.cpp @@ -124,6 +124,7 @@ TEST(ECash, SignTransaction) { // https://blockchair.com/ecash/transaction/96ee20002b34e468f9d3c5ee54f6a8ddaa61c118889c4f35395c2cd93ba5bbb4 auto input = Proto::SigningInput(); + input.set_coin_type(TWCoinTypeECash); input.set_hash_type(hashTypeForCoin(TWCoinTypeECash)); input.set_amount(amount); input.set_byte_fee(1); diff --git a/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp index 994c8977942..4134ab277f7 100644 --- a/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp +++ b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp @@ -22,6 +22,7 @@ namespace TW::Bitcoin { TEST(GroestlcoinSigning, SignP2WPKH) { Proto::SigningInput input; + input.set_coin_type(TWCoinTypeGroestlcoin); input.set_hash_type(TWBitcoinSigHashTypeAll); input.set_amount(2500); input.set_byte_fee(1); @@ -64,6 +65,7 @@ TEST(GroestlcoinSigning, SignP2WPKH) { TEST(GroestlcoinSigning, SignP2PKH) { Proto::SigningInput input; + input.set_coin_type(TWCoinTypeGroestlcoin); input.set_hash_type(TWBitcoinSigHashTypeAll); input.set_amount(2500); input.set_byte_fee(1); @@ -107,6 +109,7 @@ TEST(GroestlcoinSigning, SignP2PKH) { TEST(GroestlcoinSigning, SignP2SH_P2WPKH) { // TX outputs Proto::SigningInput input; + input.set_coin_type(TWCoinTypeGroestlcoin); input.set_hash_type(TWBitcoinSigHashTypeAll); input.set_amount(5'000); input.set_byte_fee(1); @@ -159,6 +162,7 @@ TEST(GroestlcoinSigning, SignP2SH_P2WPKH) { TEST(GroestlcoinSigning, PlanP2WPKH) { Proto::SigningInput input; + input.set_coin_type(TWCoinTypeGroestlcoin); input.set_hash_type(TWBitcoinSigHashTypeAll); input.set_amount(2500); input.set_byte_fee(1); From 2f49d26071f32cfa34e3e73257c5bce8735b30cb Mon Sep 17 00:00:00 2001 From: Ayush Bherwani Date: Mon, 9 Jan 2023 14:52:09 +0530 Subject: [PATCH 070/426] Native Injective integration (#2828) * add basic implementation of injective * add swift tests * add cpp tests --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../TestNativeInjectiveAddress.kt | 29 ++++++++ .../TestNativeInjectiveSigner.kt | 69 +++++++++++++++++++ docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 30 ++++++++ src/Cosmos/Protobuf/injective_keys.proto | 6 ++ src/Cosmos/ProtobufSerialization.cpp | 8 +++ .../Blockchains/NativeInjectiveTests.swift | 59 ++++++++++++++++ swift/Tests/CoinAddressDerivationTests.swift | 3 + tests/chains/NativeInjective/SignerTests.cpp | 55 +++++++++++++++ .../NativeInjective/TWAnyAddressTests.cpp | 20 ++++++ .../NativeInjective/TWCoinTypeTests.cpp | 37 ++++++++++ tests/common/CoinAddressDerivationTests.cpp | 3 + 14 files changed, 322 insertions(+) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveSigner.kt create mode 100644 src/Cosmos/Protobuf/injective_keys.proto create mode 100644 swift/Tests/Blockchains/NativeInjectiveTests.swift create mode 100644 tests/chains/NativeInjective/SignerTests.cpp create mode 100644 tests/chains/NativeInjective/TWAnyAddressTests.cpp create mode 100644 tests/chains/NativeInjective/TWCoinTypeTests.cpp 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 b3384921873..4e49c45c43e 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 @@ -108,6 +108,7 @@ class CoinAddressDerivationTests { APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) HEDERA -> assertEquals("0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5", address) SECRET -> assertEquals("secret1f69sk5033zcdr2p2yf3xjehn7xvgdeq09d2llh", address) + NATIVEINJECTIVE -> assertEquals("inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", address) AGORIC -> assertEquals("agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveAddress.kt new file mode 100644 index 00000000000..fc0a81474eb --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveAddress.kt @@ -0,0 +1,29 @@ +// Copyright © 2017-2022 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.nativeinjective + +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestNativeInjectiveAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("9ee18daf8e463877aaf497282abc216852420101430482a28e246c179e2c5ef1".toHexByteArray()) + val pubKey = key.getPublicKeySecp256k1(false) + val address = AnyAddress(pubKey, CoinType.NATIVEINJECTIVE) + val expected = AnyAddress("inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", CoinType.NATIVEINJECTIVE) + + assertEquals(address.description(), expected.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveSigner.kt new file mode 100644 index 00000000000..2474fb37899 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveSigner.kt @@ -0,0 +1,69 @@ +// Copyright © 2017-2022 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.nativeinjective + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.AnyAddress +import wallet.core.jni.CoinType +import wallet.core.jni.PrivateKey +import wallet.core.jni.proto.Cosmos + +class TestNativeInjectiveSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun NativeInjectiveTransactionSigning() { + val key = PrivateKey("9ee18daf8e463877aaf497282abc216852420101430482a28e246c179e2c5ef1".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(false) + val from = AnyAddress(publicKey, CoinType.NATIVEINJECTIVE).description() + + val transferAmount = Cosmos.Amount.newBuilder().apply { + amount = "10000000000" + denom = "inj" + }.build() + + val message = Cosmos.Message.newBuilder().apply { + sendCoinsMessage = Cosmos.Message.Send.newBuilder().apply { + fromAddress = from + toAddress = "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd" + addAllAmounts(listOf(transferAmount)) + }.build() + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "100000000000000" + denom = "inj" + }.build() + + val transferFee = Cosmos.Fee.newBuilder().apply { + gas = 110000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = Cosmos.SigningMode.Protobuf + accountNumber = 17396 + chainId = "injective-1" + sequence = 1 + fee = transferFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, CoinType.NATIVEINJECTIVE, Cosmos.SigningOutput.parser()) + + // https://www.mintscan.io/injective/txs/135DD2C4A1910E4334A9C0F15125DA992E724EBF23FEB9638FCB71218BB064A5 + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"Co8BCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKKmluajEzdTZnN3ZxZ3cwNzRtZ21mMnplMmNhZHp2a3o5c25sd2NydHE4YRIqaW5qMXhtcGtteHI0YXMwMGVtMjN0YzJ6Z211eXkyZ3I0aDN3Z2NsNnZkGhIKA2luahILMTAwMDAwMDAwMDASfgpeClQKLS9pbmplY3RpdmUuY3J5cHRvLnYxYmV0YTEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiEDWgxrg7i9mCflBycMrbSZt+OpCVJG9qIhMoH3g9h3yYsSBAoCCAEYARIcChYKA2luahIPMTAwMDAwMDAwMDAwMDAwELDbBhpArNDBBEwHVKwuSDozLIwvOOhDQ/i7bXC6Av5ZefSQf7RS2ejrapX/JKXPsYrtMWadhKCedomODhujvWzzGwmXVA==\"}") + } +} diff --git a/docs/registry.md b/docs/registry.md index 0d9e425c1d1..3c06e890867 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -78,6 +78,7 @@ This list is generated from [./registry.json](../registry.json) | 5718350 | Wanchain | WAN | | | | 5741564 | Waves | WAVES | | | | 10000025 | Cronos Chain | CRO | | | +| 10000060 | Native Injective | INJ | | | | 10000070 | Optimism Ethereum | ETH | | | | 10000100 | Gnosis Chain | xDAI | | | | 10000118 | Osmosis | OSMO | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index cb196d5920b..6bd2e78b158 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -126,6 +126,7 @@ enum TWCoinType { TWCoinTypeAptos = 637, TWCoinTypeHedera = 3030, TWCoinTypeSecret = 529, + TWCoinTypeNativeInjective = 10000060, TWCoinTypeAgoric = 564, }; diff --git a/registry.json b/registry.json index fabf45f3b5b..8d4d9d7f67d 100644 --- a/registry.json +++ b/registry.json @@ -2911,5 +2911,35 @@ "rpc": "https://agoric-rpc.polkachu.com", "documentation": "https://docs.agoric.com" } + }, + { + "id": "nativeinjective", + "name": "NativeInjective", + "displayName": "Native Injective", + "coinId": 10000060, + "symbol": "INJ", + "decimals": 18, + "blockchain": "Cosmos", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "hrp": "inj", + "addressHasher": "keccak256", + "chainId": "injective-1", + "explorer": { + "url": "https://www.mintscan.io/injective", + "txPath": "/txs/", + "accountPath": "/account/", + "sampleTx": "C5F6A4FF9DF1AE9FF543D2CEBD8E3E9B04290B2445F9D91D7707EDBF4B7EE16B", + "sampleAccount": "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd" + }, + "info": { + "url": "https://injective.com", + "documentation": "https://docs.injective.network" + } } ] diff --git a/src/Cosmos/Protobuf/injective_keys.proto b/src/Cosmos/Protobuf/injective_keys.proto new file mode 100644 index 00000000000..bc11f94d667 --- /dev/null +++ b/src/Cosmos/Protobuf/injective_keys.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; +package injective.crypto.v1beta1.ethsecp256k1; + +message PubKey { + bytes key = 1; +} diff --git a/src/Cosmos/ProtobufSerialization.cpp b/src/Cosmos/ProtobufSerialization.cpp index e2f2e070890..89eff73b63c 100644 --- a/src/Cosmos/ProtobufSerialization.cpp +++ b/src/Cosmos/ProtobufSerialization.cpp @@ -20,6 +20,7 @@ #include "Protobuf/terra_wasm_v1beta1_tx.pb.h" #include "Protobuf/thorchain_bank_tx.pb.h" #include "Protobuf/ethermint_keys.pb.h" +#include "Protobuf/injective_keys.pb.h" #include "PrivateKey.h" #include "Data.h" @@ -335,6 +336,12 @@ std::string buildAuthInfo(const Proto::SigningInput& input, TWCoinType coin) { signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); break; } + case TWCoinTypeNativeInjective: { + auto pubKey = injective::crypto::v1beta1::ethsecp256k1::PubKey(); + pubKey.set_key(publicKey.bytes.data(), publicKey.bytes.size()); + signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); + break; + } default: { auto pubKey = cosmos::crypto::secp256k1::PubKey(); pubKey.set_key(publicKey.bytes.data(), publicKey.bytes.size()); @@ -365,6 +372,7 @@ Data buildSignature(const Proto::SigningInput& input, const std::string& seriali Data hashToSign; switch(coin) { + case TWCoinTypeNativeInjective: case TWCoinTypeNativeEvmos: { hashToSign = Hash::keccak256(serializedSignDoc); break; diff --git a/swift/Tests/Blockchains/NativeInjectiveTests.swift b/swift/Tests/Blockchains/NativeInjectiveTests.swift new file mode 100644 index 00000000000..6a95b53e85f --- /dev/null +++ b/swift/Tests/Blockchains/NativeInjectiveTests.swift @@ -0,0 +1,59 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class NativeInjectiveTests: XCTestCase { + + func testAddress() { + let key = PrivateKey(data: Data(hexString: "9ee18daf8e463877aaf497282abc216852420101430482a28e246c179e2c5ef1")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: false) + let address = AnyAddress(publicKey: pubkey, coin: .nativeInjective) + let addressFromString = AnyAddress(string: "inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", coin: .nativeInjective)! + + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSign() { + let privateKey = PrivateKey(data: Data(hexString: "9ee18daf8e463877aaf497282abc216852420101430482a28e246c179e2c5ef1")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: false) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .nativeInjective) + + let message = CosmosMessage.with { + $0.sendCoinsMessage = CosmosMessage.Send.with { + $0.fromAddress = fromAddress.description + $0.toAddress = "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd" + $0.amounts = [CosmosAmount.with { + $0.amount = "10000000000" + $0.denom = "inj" + }] + } + } + + let fee = CosmosFee.with { + $0.gas = 110000 + $0.amounts = [CosmosAmount.with { + $0.amount = "100000000000000" + $0.denom = "inj" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 17396 + $0.chainID = "injective-1" + $0.sequence = 1 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .nativeInjective) + + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"Co8BCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKKmluajEzdTZnN3ZxZ3cwNzRtZ21mMnplMmNhZHp2a3o5c25sd2NydHE4YRIqaW5qMXhtcGtteHI0YXMwMGVtMjN0YzJ6Z211eXkyZ3I0aDN3Z2NsNnZkGhIKA2luahILMTAwMDAwMDAwMDASfgpeClQKLS9pbmplY3RpdmUuY3J5cHRvLnYxYmV0YTEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiEDWgxrg7i9mCflBycMrbSZt+OpCVJG9qIhMoH3g9h3yYsSBAoCCAEYARIcChYKA2luahIPMTAwMDAwMDAwMDAwMDAwELDbBhpArNDBBEwHVKwuSDozLIwvOOhDQ/i7bXC6Av5ZefSQf7RS2ejrapX/JKXPsYrtMWadhKCedomODhujvWzzGwmXVA==\"}") + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index d839b605452..6b5a84791be 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -262,6 +262,9 @@ class CoinAddressDerivationTests: XCTestCase { case .secret: let expectedResult = "secret1f69sk5033zcdr2p2yf3xjehn7xvgdeq09d2llh" assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .nativeInjective: + let expectedResult = "inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .agoric: let expectedResult = "agoric18zvvgk6j3eq5wd7mqxccgt20gz2w94cy88aek5" assertCoinDerivation(coin, expectedResult, derivedAddress, address) diff --git a/tests/chains/NativeInjective/SignerTests.cpp b/tests/chains/NativeInjective/SignerTests.cpp new file mode 100644 index 00000000000..68b8789a1d2 --- /dev/null +++ b/tests/chains/NativeInjective/SignerTests.cpp @@ -0,0 +1,55 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "Base64.h" +#include "proto/Cosmos.pb.h" +#include "Cosmos/Address.h" +#include "Cosmos/Signer.h" +#include "TestUtilities.h" +#include + +#include +#include + +namespace TW::Cosmos::nativeInjective::tests { + +TEST(NativeInjectiveSigner, Sign) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(17396); + input.set_chain_id("injective-1"); + input.set_sequence(1); + + Address fromAddress; + Address toAddress; + EXPECT_TRUE(Address::decode("inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", fromAddress)); + EXPECT_TRUE(Address::decode("inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("inj"); + amountOfTx->set_amount("10000000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(110000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("inj"); + amountOfFee->set_amount("100000000000000"); + + auto privateKey = parse_hex("9ee18daf8e463877aaf497282abc216852420101430482a28e246c179e2c5ef1"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeNativeInjective); + + // https://www.mintscan.io/injective/txs/135DD2C4A1910E4334A9C0F15125DA992E724EBF23FEB9638FCB71218BB064A5 + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"Co8BCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKKmluajEzdTZnN3ZxZ3cwNzRtZ21mMnplMmNhZHp2a3o5c25sd2NydHE4YRIqaW5qMXhtcGtteHI0YXMwMGVtMjN0YzJ6Z211eXkyZ3I0aDN3Z2NsNnZkGhIKA2luahILMTAwMDAwMDAwMDASfgpeClQKLS9pbmplY3RpdmUuY3J5cHRvLnYxYmV0YTEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiEDWgxrg7i9mCflBycMrbSZt+OpCVJG9qIhMoH3g9h3yYsSBAoCCAEYARIcChYKA2luahIPMTAwMDAwMDAwMDAwMDAwELDbBhpArNDBBEwHVKwuSDozLIwvOOhDQ/i7bXC6Av5ZefSQf7RS2ejrapX/JKXPsYrtMWadhKCedomODhujvWzzGwmXVA==\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); +} + +} diff --git a/tests/chains/NativeInjective/TWAnyAddressTests.cpp b/tests/chains/NativeInjective/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..d2f2395ccd5 --- /dev/null +++ b/tests/chains/NativeInjective/TWAnyAddressTests.cpp @@ -0,0 +1,20 @@ +// Copyright © 2017-2022 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 +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TWNativeInjective, Address) { + auto string = STRING("inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeNativeInjective)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); +} diff --git a/tests/chains/NativeInjective/TWCoinTypeTests.cpp b/tests/chains/NativeInjective/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..15405de06e9 --- /dev/null +++ b/tests/chains/NativeInjective/TWCoinTypeTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 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. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWNativeInjectiveCoinType, TWCoinType) { + const auto coin = TWCoinTypeNativeInjective; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("C5F6A4FF9DF1AE9FF543D2CEBD8E3E9B04290B2445F9D91D7707EDBF4B7EE16B")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "nativeinjective"); + assertStringsEqual(name, "Native Injective"); + assertStringsEqual(symbol, "INJ"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "injective-1"); + assertStringsEqual(txUrl, "https://www.mintscan.io/injective/txs/C5F6A4FF9DF1AE9FF543D2CEBD8E3E9B04290B2445F9D91D7707EDBF4B7EE16B"); + assertStringsEqual(accUrl, "https://www.mintscan.io/injective/account/inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 64d792feea9..167041878b2 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -258,6 +258,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeSecret: EXPECT_EQ(address, "secret1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0m7t23a"); break; + case TWCoinTypeNativeInjective: + EXPECT_EQ(address, "inj1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj0knl55v"); + break; case TWCoinTypeAgoric: EXPECT_EQ(address, "agoric1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0txauuh"); break; From e15919afd85d1bfe38295e2c216b2fac9a032363 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 9 Jan 2023 12:58:55 +0100 Subject: [PATCH 071/426] misc(CODEOWNERS): update CODEOWNERS (#2861) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 707fa9c11ea..61760e1454c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,4 +3,4 @@ # @global-owner1 and @global-owner2 will be requested for # review when someone opens a pull request. -* @hewigovens @catenocrypt @milerius @miloserdow +* @hewigovens @catenocrypt @milerius From c452dc7554516ecdcb88a9bedcb4096bb1cf8b9a Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 10 Jan 2023 11:12:11 +0100 Subject: [PATCH 072/426] feat(thorswap): SWAP from atom support (#2860) --- src/THORChain/Swap.cpp | 37 ++++++++++++++++++++ src/THORChain/Swap.h | 4 ++- tests/chains/THORChain/SwapTests.cpp | 51 ++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index e23c4ec999a..0371c6dfc61 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -9,6 +9,9 @@ #include "Coin.h" #include +// ATOM +#include "Cosmos/Address.h" +#include "../proto/Cosmos.pb.h" // BTC #include "Bitcoin/SigHashType.h" #include "../proto/Bitcoin.pb.h" @@ -56,6 +59,8 @@ TWCoinType chainCoinType(Chain chain) { return TWCoinTypeBitcoinCash; case Chain::LTC: return TWCoinTypeLitecoin; + case Chain::ATOM: + return TWCoinTypeCosmos; case Chain::THOR: default: return TWCoinTypeTHORChain; @@ -76,6 +81,8 @@ std::string chainName(Chain chain) { return "BCH"; case Chain::LTC: return "LTC"; + case Chain::ATOM: + return "ATOM"; case Chain::THOR: default: return "THOR"; @@ -108,6 +115,8 @@ SwapBundled SwapBuilder::build(bool shortened) { return buildBitcoin(fromAmountNum, memo, fromChain); case Chain::BNB: return buildBinance(mFromAsset, fromAmountNum, memo); + case Chain::ATOM: + return buildAtom(fromAmountNum, memo); case Chain::ETH: return buildEth(fromAmountNum, memo); } @@ -254,4 +263,32 @@ SwapBundled SwapBuilder::buildEth(uint64_t amount, const std::string& memo) { out.insert(out.end(), serialized.begin(), serialized.end()); return {.out = std::move(out)}; } + +SwapBundled SwapBuilder::buildAtom(uint64_t amount, const std::string& memo) { + if (!Cosmos::Address::isValid(mVaultAddress, "cosmos")) { + return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_vault_address), .error = "Invalid vault address: " + mVaultAddress}; + } + Data out; + + auto input = Cosmos::Proto::SigningInput(); + input.set_signing_mode(Cosmos::Proto::Protobuf); + input.set_chain_id("cosmoshub-4"); + input.set_memo(memo); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + + message.set_from_address(mFromAddress); + message.set_to_address(mVaultAddress); + + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("uatom"); + amountOfTx->set_amount(std::to_string(amount)); + + auto serialized = input.SerializeAsString(); + out.insert(out.end(), serialized.begin(), serialized.end()); + + return {.out = std::move(out)}; +} + } // namespace TW::THORChainSwap diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h index 504553567ef..aca8ce154aa 100644 --- a/src/THORChain/Swap.h +++ b/src/THORChain/Swap.h @@ -23,7 +23,8 @@ enum Chain { BNB = 3, DOGE = 4, BCH = 5, - LTC = 6 + LTC = 6, + ATOM = 7 }; using SwapErrorCode = int; @@ -50,6 +51,7 @@ class SwapBuilder { SwapBundled buildBitcoin(uint64_t amount, const std::string& memo, Chain fromChain); SwapBundled buildBinance(Proto::Asset fromAsset, uint64_t amount, const std::string& memo); SwapBundled buildEth(uint64_t amount, const std::string& memo); + SwapBundled buildAtom(uint64_t amount, const std::string& memo); public: SwapBuilder() noexcept = default; diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 54bb152bb3c..1c0a86ec1a4 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -13,6 +13,7 @@ #include "Ethereum/Address.h" #include "THORChain/Swap.h" #include "proto/Binance.pb.h" +#include "proto/Cosmos.pb.h" #include "proto/Bitcoin.pb.h" #include "proto/Ethereum.pb.h" #include "proto/THORChainSwap.pb.h" @@ -373,6 +374,56 @@ TEST(THORChainSwap, SwapBtcBnb) { // https://explorer.binance.org/tx/8D78469069118E9B9546696214CCD46E63D3FA0D7E854C094D63C8F6061278B7 } +TEST(THORChainSwap, SwapAtomBnb) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::ATOM)); + fromAsset.set_symbol("ATOM"); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress("cosmos1v4e6vpehwrfez2dqepnw9g6t4fl83xzegd5ac9") + .toAddress("bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2") + .vault("cosmos154t5ycejlr7ax3ynmed9z05yg5a27y9u6pj5hq") + .fromAmount("300000") + .toAmountLimit("819391") + .affFeeAddress("t") + .affFeeRate("0") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "08011a0b636f736d6f736875622d342a3f3d3a424e422e424e423a626e623173346b616c6c786e67707973707a6d366e72657a6b6d6c3972677977366b78707734666872323a3831393339313a743a3042710a6f0a2d636f736d6f73317634653676706568777266657a32647165706e773967367434666c3833787a65676435616339122d636f736d6f7331353474357963656a6c7237617833796e6d6564397a303579673561323779397536706a3568711a0f0a057561746f6d1206333030303030"); + + auto tx = Cosmos::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); + ASSERT_EQ(tx.memo(), "=:BNB.BNB:bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2:819391:t:0"); + + auto& fee = *tx.mutable_fee(); + fee.set_gas(200000); + auto& fee_amount = *fee.add_amounts(); + fee_amount.set_denom("uatom"); + fee_amount.set_amount("500"); + + + tx.set_account_number(1483163); + tx.set_sequence(1); + + auto privKey = parse_hex("3eed3f32b8ba90e579ba46f37e7445fb4b34558306aa5bc32c525a93dff486e7"); + tx.set_private_key(privKey.data(), privKey.size()); + + Cosmos::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeCosmos); + EXPECT_EQ(output.error(), ""); + ASSERT_EQ(output.serialized(), "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CtMBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczF2NGU2dnBlaHdyZmV6MmRxZXBudzlnNnQ0Zmw4M3h6ZWdkNWFjORItY29zbW9zMTU0dDV5Y2VqbHI3YXgzeW5tZWQ5ejA1eWc1YTI3eTl1NnBqNWhxGg8KBXVhdG9tEgYzMDAwMDASPz06Qk5CLkJOQjpibmIxczRrYWxseG5ncHlzcHptNm5yZXprbWw5cmd5dzZreHB3NGZocjI6ODE5MzkxOnQ6MBJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDmmNIYBvR9bnOloFEMOWdk9DHYIGe7naW0T19y+/k1SUSBAoCCAEYARISCgwKBXVhdG9tEgM1MDAQwJoMGkCFqUWtDu0pn1P/cnVQnIJIWF8HFJn/xkJh55Mc7ZLVPF60uXYUOg8nNkt0IQPuTFREw32/yff6lmA5w6KwPen/\"}"); + + // https://viewblock.io/thorchain/tx/07F47D71A74245538E205F24ADB4BBB799B49C3A8A8875665D249EA51662AA50 + // https://www.mintscan.io/cosmos/txs/07F47D71A74245538E205F24ADB4BBB799B49C3A8A8875665D249EA51662AA50 + // https://binance.mintscan.io/txs/2C97061737B16B234990B9B18A2BF65F7C7418FF9E39A68E634C832E4E4C59CE +} + Data SwapTest_ethAddressStringToData(const std::string& asString) { if (asString.empty()) { return Data(); From 7a372b216b29d3c025e4769515a56c9f14bfa5fe Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 11 Jan 2023 08:37:39 +0100 Subject: [PATCH 073/426] feat(thorswap): add full avax support (#2864) --- src/THORChain/Swap.cpp | 5 +++ src/THORChain/Swap.h | 3 +- tests/chains/THORChain/SwapTests.cpp | 62 ++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index 0371c6dfc61..8cf5c44be2d 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -49,6 +49,8 @@ TWCoinType chainCoinType(Chain chain) { switch (chain) { case Chain::ETH: return TWCoinTypeEthereum; + case Chain::AVAX: + return TWCoinTypeAvalancheCChain; case Chain::BNB: return TWCoinTypeBinance; case Chain::BTC: @@ -69,6 +71,8 @@ TWCoinType chainCoinType(Chain chain) { std::string chainName(Chain chain) { switch (chain) { + case Chain::AVAX: + return "AVAX"; case Chain::ETH: return "ETH"; case Chain::BNB: @@ -118,6 +122,7 @@ SwapBundled SwapBuilder::build(bool shortened) { case Chain::ATOM: return buildAtom(fromAmountNum, memo); case Chain::ETH: + case Chain::AVAX: return buildEth(fromAmountNum, memo); } default: diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h index aca8ce154aa..b8bedea7a76 100644 --- a/src/THORChain/Swap.h +++ b/src/THORChain/Swap.h @@ -24,7 +24,8 @@ enum Chain { DOGE = 4, BCH = 5, LTC = 6, - ATOM = 7 + ATOM = 7, + AVAX = 8 }; using SwapErrorCode = int; diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 1c0a86ec1a4..2ecd1094172 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -499,6 +499,68 @@ TEST(THORChainSwap, SwapErc20Rune) { // https://viewblock.io/thorchain/tx/BC1464CF3B56B07E40CF57985511814AEC9EAE2F1329CEE059A21529FDDFDB8C } +TEST(THORChainSwap, SwapAvaxBnb) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::AVAX)); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BNB)); + toAsset.set_symbol("BNB"); + auto && [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress("0x4340fb8bb31357559607142b37C2173418E36785") + .toAddress("bnb1hy9a63tgsvham463vw2tkkw2j5y20v5drhw8p2") + .vault("0x53595320f158d4546677B4795Cc66dfF59D154Db") + .router("0x8f66c4ae756bebc49ec8b81966dd8bba9f127549") + .fromAmount("20000000000000000") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + EXPECT_EQ(hex(out), "0a01001201002201002a0100422a30783866363663346165373536626562633439656338623831393636646438626261396631323735343952f30132f0010a07470de4df82000012e4011fece7b400000000000000000000000053595320f158d4546677b4795cc66dff59d154db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000343d3a424e422e424e423a626e62316879396136337467737668616d343633767732746b6b77326a35793230763564726877387032000000000000000000000000"); + + auto tx = Ethereum::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); + + // check fields + EXPECT_EQ(tx.to_address(), "0x8f66c4ae756bebc49ec8b81966dd8bba9f127549"); + ASSERT_TRUE(tx.transaction().has_contract_generic()); + + Data vaultAddressBin = SwapTest_ethAddressStringToData("0x53595320f158d4546677B4795Cc66dfF59D154Db"); + EXPECT_EQ(hex(vaultAddressBin), "53595320f158d4546677b4795cc66dff59d154db"); + auto func = Ethereum::ABI::Function("deposit", std::vector>{ + std::make_shared(vaultAddressBin), + std::make_shared(parse_hex("0000000000000000000000000000000000000000")), + std::make_shared(uint256_t(20000000000000000)), + std::make_shared("=:BNB.BNB:bnb1hy9a63tgsvham463vw2tkkw2j5y20v5drhw8p2")}); + Data payload; + func.encode(payload); + EXPECT_EQ(hex(payload), "1fece7b400000000000000000000000053595320f158d4546677b4795cc66dff59d154db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000343d3a424e422e424e423a626e62316879396136337467737668616d343633767732746b6b77326a35793230763564726877387032000000000000000000000000"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "470de4df820000"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); + + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set few fields before signing + auto chainId = store(uint256_t(43114)); + tx.set_chain_id(chainId.data(), chainId.size()); + auto nonce = store(uint256_t(4)); + tx.set_nonce(nonce.data(), nonce.size()); + auto gasPrice = store(uint256_t(30000000000)); + tx.set_gas_price(gasPrice.data(), gasPrice.size()); + auto gasLimit = store(uint256_t(108810)); + tx.set_gas_limit(gasLimit.data(), gasLimit.size()); + auto privKey = parse_hex("42dcdf1cc2020a98f874a2f754eeb127e556ee62714973299e62ebdaadb48218"); + tx.set_private_key(privKey.data(), privKey.size()); + + // sign and encode resulting input + Ethereum::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeAvalancheCChain); + EXPECT_EQ(hex(output.encoded()), "f90154048506fc23ac008301a90a948f66c4ae756bebc49ec8b81966dd8bba9f12754987470de4df820000b8e41fece7b400000000000000000000000053595320f158d4546677b4795cc66dff59d154db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000343d3a424e422e424e423a626e62316879396136337467737668616d343633767732746b6b77326a35793230763564726877387032000000000000000000000000830150f8a09491c6b06fc1773bfc2a067787da96143f3d56eaf9fa9667298fdb298d33944ea04e2e2618e7c822b21af1ccc27746bfa46fa703b1c7f14cb0e33bd7f88acf2045"); + // https://viewblock.io/thorchain/tx/8A29B132443BF1B0A0BD3E00F8155D10FEEEC7737BDC912C4A1AFB0A52E4FD4F + // https://snowtrace.io/tx/0x8A29B132443BF1B0A0BD3E00F8155D10FEEEC7737BDC912C4A1AFB0A52E4FD4F + // https://binance.mintscan.io/txs/9D250C8BAC8205B942A597AFB345045439A55CAB8DD588B75870D4E47D751C16 +} + TEST(THORChainSwap, SwapEthBnb) { Proto::Asset fromAsset; fromAsset.set_chain(static_cast(Chain::ETH)); From 9d1e8abfec727f0ca2a26aadd3cfd2d7eff68026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Wed, 11 Jan 2023 12:07:28 +0200 Subject: [PATCH 074/426] Rename Elrond to MultiversX (configuration change) (#2862) * Rename Elrond to MultiversX. * Fix logo URL, fix example URLs. --- .../core/app/blockchains/elrond/TestElrondSigner.kt | 4 ++-- docs/registry.md | 2 +- registry.json | 13 +++++++------ src/Elrond/Codec.cpp | 2 +- src/Elrond/Codec.h | 6 +++--- src/Elrond/NetworkConfig.h | 4 ++-- src/Elrond/TransactionFactory.h | 12 ++++++------ swift/Tests/Blockchains/ElrondTests.swift | 4 ++-- tests/chains/Elrond/SignerTests.cpp | 4 ++-- tests/chains/Elrond/TWCoinTypeTests.cpp | 10 +++++----- 10 files changed, 31 insertions(+), 30 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt index 44a9e16b2a3..a3597e4deb0 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt @@ -59,7 +59,7 @@ class TestElrondSigner { @Test fun signGenericActionUndelegate() { - // Successfully broadcasted https://explorer.elrond.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 + // Successfully broadcasted https://explorer.multiversx.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) val accounts = Elrond.Accounts.newBuilder() @@ -92,7 +92,7 @@ class TestElrondSigner { @Test fun signGenericActionDelegate() { - // Successfully broadcasted https://explorer.elrond.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 + // Successfully broadcasted https://explorer.multiversx.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) val accounts = Elrond.Accounts.newBuilder() diff --git a/docs/registry.md b/docs/registry.md index 3c06e890867..823c1526609 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -49,7 +49,7 @@ This list is generated from [./registry.json](../registry.json) | 494 | BandChain | BAND | | | | 500 | Theta | THETA | | | | 501 | Solana | SOL | | | -| 508 | Elrond | eGLD | | | +| 508 | MultiversX | eGLD | | | | 529 | Secret | SCRT | | | | 564 | Agoric | BLD | | | | 637 | Aptos | APT | | | diff --git a/registry.json b/registry.json index 8d4d9d7f67d..8bc0bedea72 100644 --- a/registry.json +++ b/registry.json @@ -1352,6 +1352,7 @@ { "id": "elrond", "name": "Elrond", + "displayName": "MultiversX", "coinId": 508, "symbol": "eGLD", "decimals": 18, @@ -1365,15 +1366,15 @@ "publicKeyType": "ed25519", "hrp": "erd", "explorer": { - "url": "https://explorer.elrond.com", + "url": "https://explorer.multiversx.com", "txPath": "/transactions/", - "accountPath": "/address/" + "accountPath": "/accounts/" }, "info": { - "url": "https://elrond.com/", - "source": "https://github.com/ElrondNetwork/elrond-go", - "rpc": "https://api.elrond.com", - "documentation": "https://docs.elrond.com" + "url": "https://multiversx.com/", + "source": "https://github.com/multiversx/mx-chain-go", + "rpc": "https://api.multiversx.com", + "documentation": "https://docs.multiversx.com" } }, { diff --git a/src/Elrond/Codec.cpp b/src/Elrond/Codec.cpp index dbdf0859291..a76328f8b39 100644 --- a/src/Elrond/Codec.cpp +++ b/src/Elrond/Codec.cpp @@ -25,7 +25,7 @@ std::string Codec::encodeBigInt(const std::string& value) { return encodeBigInt(uint256_t(value)); } -// For reference, see https://docs.elrond.com/developers/developer-reference/elrond-serialization-format/#arbitrary-width-big-numbers. +// For reference, see https://docs.multiversx.com/developers/developer-reference/elrond-serialization-format/#arbitrary-width-big-numbers. std::string Codec::encodeBigInt(uint256_t value) { std::string encoded = hex(store(value)); return encoded; diff --git a/src/Elrond/Codec.h b/src/Elrond/Codec.h index b2ffd73405b..18d71f3f223 100644 --- a/src/Elrond/Codec.h +++ b/src/Elrond/Codec.h @@ -13,9 +13,9 @@ namespace TW::Elrond { /// A stripped-down variant of the Elrond codec. /// For reference, see: -/// - https://docs.elrond.com/developers/developer-reference/elrond-serialization-format -/// - https://github.com/ElrondNetwork/elrond-sdk-erdjs/tree/main/src/smartcontracts/codec -/// - https://github.com/ElrondNetwork/elrond-wasm-rs/tree/master/elrond-codec +/// - https://docs.multiversx.com/developers/developer-reference/overview +/// - https://github.com/multiversx/mx-sdk-erdjs/tree/main/src/smartcontracts/codec +/// - https://github.com/multiversx/mx-sdk-rs/tree/master/framework/codec class Codec { public: static std::string encodeString(const std::string& value); diff --git a/src/Elrond/NetworkConfig.h b/src/Elrond/NetworkConfig.h index 79b5ebf61d0..719335b17ed 100644 --- a/src/Elrond/NetworkConfig.h +++ b/src/Elrond/NetworkConfig.h @@ -12,8 +12,8 @@ namespace TW::Elrond { /// A "NetworkConfig" object holds the network parameters relevant to creating transactions (e.g. minimum gas limit, minimum gas price). class NetworkConfig { - /// The following fields can (should) be fetched from https://api.elrond.com/network/config. - /// However, a "NetworkConfig" object is initialized with proper default values for Elrond Mainnet (as of December 2021). + /// The following fields can (should) be fetched from https://api.multiversx.com/network/config. + /// However, a "NetworkConfig" object is initialized with proper default values for Mainnet (as of December 2021). std::string chainId; uint32_t gasPerDataByte; uint32_t minGasLimit; diff --git a/src/Elrond/TransactionFactory.h b/src/Elrond/TransactionFactory.h index db3b3058b9d..db7ec70e19c 100644 --- a/src/Elrond/TransactionFactory.h +++ b/src/Elrond/TransactionFactory.h @@ -30,20 +30,20 @@ class TransactionFactory { Transaction fromGenericAction(const Proto::SigningInput& input); /// This should be used to transfer EGLD. - /// For reference, see: https://docs.elrond.com/developers/signing-transactions/signing-transactions/#general-structure. + /// For reference, see: https://docs.multiversx.com/developers/signing-transactions/signing-transactions. Transaction fromEGLDTransfer(const Proto::SigningInput& input); /// This should be used to transfer regular ESDTs (fungible tokens). - /// For reference, see: https://docs.elrond.com/developers/esdt-tokens/#transfers + /// For reference, see: https://docs.multiversx.com/developers/esdt-tokens /// - /// The "regular" ESDT tokens held by an account can be fetched from https://api.elrond.com/accounts/{address}/tokens. + /// The "regular" ESDT tokens held by an account can be fetched from https://api.multiversx.com/accounts/{address}/tokens. Transaction fromESDTTransfer(const Proto::SigningInput& input); /// This should be used to transfer NFTs, SFTs and Meta ESDTs. - /// For reference, see: https://docs.elrond.com/developers/nft-tokens/#transfers + /// For reference, see: https://docs.multiversx.com/developers/nft-tokens /// - /// The semi-fungible and non-fungible tokens held by an account can be fetched from https://api.elrond.com/accounts/{address}/nfts?type=SemiFungibleESDT,NonFungibleESDT. - /// The Meta ESDTs (a special kind of SFTs) held by an account can be fetched from https://api.elrond.com/accounts/{address}/nfts?type=MetaESDT. + /// The semi-fungible and non-fungible tokens held by an account can be fetched from https://api.multiversx.com/accounts/{address}/nfts?type=SemiFungibleESDT,NonFungibleESDT. + /// The Meta ESDTs (a special kind of SFTs) held by an account can be fetched from https://api.multiversx.com/accounts/{address}/nfts?type=MetaESDT. /// /// The fields "token_collection" and "token_nonce" are found as well in the HTTP response of the API call (as "collection" and "nonce", respectively). Transaction fromESDTNFTTransfer(const Proto::SigningInput& input); diff --git a/swift/Tests/Blockchains/ElrondTests.swift b/swift/Tests/Blockchains/ElrondTests.swift index beee15a7a26..fa6a64ee3ca 100644 --- a/swift/Tests/Blockchains/ElrondTests.swift +++ b/swift/Tests/Blockchains/ElrondTests.swift @@ -53,7 +53,7 @@ class ElrondTests: XCTestCase { } func testSignGenericActionUndelegate() { - // Successfully broadcasted https://explorer.elrond.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 + // Successfully broadcasted https://explorer.multiversx.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! let input = ElrondSigningInput.with { @@ -82,7 +82,7 @@ class ElrondTests: XCTestCase { } func testSignGenericActionDelegate() { - // Successfully broadcasted https://explorer.elrond.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 + // Successfully broadcasted https://explorer.multiversx.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! let input = ElrondSigningInput.with { diff --git a/tests/chains/Elrond/SignerTests.cpp b/tests/chains/Elrond/SignerTests.cpp index f2348dbe155..12172fa73d8 100644 --- a/tests/chains/Elrond/SignerTests.cpp +++ b/tests/chains/Elrond/SignerTests.cpp @@ -80,7 +80,7 @@ TEST(ElrondSigner, SignGenericActionUnDelegate) { })"_json; assertJSONEqual(expected, nlohmann::json::parse(encoded)); ASSERT_EQ(expectedSignature, signature); - // Successfully broadcasted https://explorer.elrond.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 + // Successfully broadcasted https://explorer.multiversx.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 } TEST(ElrondSigner, SignGenericActionRedelegateRewards) { @@ -226,7 +226,7 @@ TEST(ElrondSigner, SignGenericActionDelegate) { })"_json; assertJSONEqual(expected, nlohmann::json::parse(encoded)); ASSERT_EQ(expectedSignature, signature); - // Successfully broadcasted https://explorer.elrond.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 + // Successfully broadcasted https://explorer.multiversx.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 } TEST(ElrondSigner, SignGenericActionJSON) { diff --git a/tests/chains/Elrond/TWCoinTypeTests.cpp b/tests/chains/Elrond/TWCoinTypeTests.cpp index 1f7a977bdc0..5502507c216 100644 --- a/tests/chains/Elrond/TWCoinTypeTests.cpp +++ b/tests/chains/Elrond/TWCoinTypeTests.cpp @@ -15,9 +15,9 @@ TEST(TWElrondCoinType, TWCoinType) { auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeElrond)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8")); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("163b46551a74626415074b626d2f37d3c78aef0f6ccb628db434ee65a35ea127")); auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeElrond, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj")); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")); auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeElrond, accId.get())); auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeElrond)); auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeElrond)); @@ -27,8 +27,8 @@ TEST(TWElrondCoinType, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeElrond)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeElrond)); assertStringsEqual(symbol, "eGLD"); - assertStringsEqual(txUrl, "https://explorer.elrond.com/transactions/1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8"); - assertStringsEqual(accUrl, "https://explorer.elrond.com/address/erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj"); + assertStringsEqual(txUrl, "https://explorer.multiversx.com/transactions/163b46551a74626415074b626d2f37d3c78aef0f6ccb628db434ee65a35ea127"); + assertStringsEqual(accUrl, "https://explorer.multiversx.com/accounts/erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); assertStringsEqual(id, "elrond"); - assertStringsEqual(name, "Elrond"); + assertStringsEqual(name, "MultiversX"); } From ad351ab9e1de23bcbfbb3284db0cc7fc682b133b Mon Sep 17 00:00:00 2001 From: stspbu Date: Fri, 13 Jan 2023 17:31:27 +0400 Subject: [PATCH 075/426] TON support (#2813) --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../TestTheOpenNetworkAddress.kt | 50 ++++++ .../TestTheOpenNetworkSigner.kt | 50 ++++++ docs/registry.md | 1 + include/TrustWalletCore/TWBlockchain.h | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 28 +++ src/Base64.cpp | 20 +++ src/Base64.h | 3 + src/Coin.cpp | 3 + src/Everscale/Address.cpp | 45 +---- src/Everscale/Address.h | 27 ++- src/Everscale/{ => CommonTON}/Cell.cpp | 6 +- src/Everscale/{ => CommonTON}/Cell.h | 6 +- src/Everscale/{ => CommonTON}/CellBuilder.cpp | 6 +- src/Everscale/{ => CommonTON}/CellBuilder.h | 4 +- src/Everscale/{ => CommonTON}/CellSlice.cpp | 6 +- src/Everscale/{ => CommonTON}/CellSlice.h | 4 +- src/Everscale/CommonTON/Messages.cpp | 76 ++++++++ src/Everscale/CommonTON/Messages.h | 78 ++++++++ src/Everscale/CommonTON/RawAddress.cpp | 103 +++++++++++ src/Everscale/CommonTON/RawAddress.h | 42 +++++ src/Everscale/CommonTON/WorkchainType.h | 16 ++ src/Everscale/Messages.cpp | 87 ++------- src/Everscale/Messages.h | 65 +------ src/Everscale/Signer.cpp | 11 +- src/Everscale/Wallet.cpp | 25 +-- src/Everscale/Wallet.h | 21 +-- src/Everscale/WorkchainType.h | 9 +- src/HexCoding.h | 9 + src/TheOpenNetwork/Address.cpp | 125 +++++++++++++ src/TheOpenNetwork/Address.h | 63 +++++++ src/TheOpenNetwork/Entry.cpp | 33 ++++ src/TheOpenNetwork/Entry.h | 21 +++ src/TheOpenNetwork/Message.cpp | 38 ++++ src/TheOpenNetwork/Message.h | 28 +++ src/TheOpenNetwork/Signer.cpp | 65 +++++++ src/TheOpenNetwork/Signer.h | 30 ++++ src/TheOpenNetwork/WorkchainType.h | 13 ++ src/TheOpenNetwork/wallet/Wallet.cpp | 94 ++++++++++ src/TheOpenNetwork/wallet/Wallet.h | 59 +++++++ src/TheOpenNetwork/wallet/WalletV4R2.cpp | 54 ++++++ src/TheOpenNetwork/wallet/WalletV4R2.h | 24 +++ src/proto/TheOpenNetwork.proto | 75 ++++++++ .../Blockchains/TheOpenNetworkTests.swift | 62 +++++++ swift/Tests/CoinAddressDerivationTests.swift | 3 + tests/chains/Everscale/CellBuilderTest.cpp | 5 +- tests/chains/Everscale/CellTests.cpp | 10 +- tests/chains/TheOpenNetwork/AddressTests.cpp | 116 ++++++++++++ tests/chains/TheOpenNetwork/SignerTests.cpp | 166 ++++++++++++++++++ .../TheOpenNetwork/TWAnyAddressTests.cpp | 33 ++++ .../TheOpenNetwork/TWAnySignerTests.cpp | 37 ++++ .../chains/TheOpenNetwork/TWCoinTypeTests.cpp | 36 ++++ tests/common/Base64Tests.cpp | 11 ++ tests/common/CoinAddressDerivationTests.cpp | 3 + tests/common/CoinAddressValidationTests.cpp | 7 + tests/common/HexCodingTests.cpp | 7 + wasm/tests/Blockchain/TheOpenNetwork.test.ts | 100 +++++++++++ 58 files changed, 1864 insertions(+), 258 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt rename src/Everscale/{ => CommonTON}/Cell.cpp (99%) rename src/Everscale/{ => CommonTON}/Cell.h (96%) rename src/Everscale/{ => CommonTON}/CellBuilder.cpp (98%) rename src/Everscale/{ => CommonTON}/CellBuilder.h (96%) rename src/Everscale/{ => CommonTON}/CellSlice.cpp (94%) rename src/Everscale/{ => CommonTON}/CellSlice.h (92%) create mode 100644 src/Everscale/CommonTON/Messages.cpp create mode 100644 src/Everscale/CommonTON/Messages.h create mode 100644 src/Everscale/CommonTON/RawAddress.cpp create mode 100644 src/Everscale/CommonTON/RawAddress.h create mode 100644 src/Everscale/CommonTON/WorkchainType.h create mode 100644 src/TheOpenNetwork/Address.cpp create mode 100644 src/TheOpenNetwork/Address.h create mode 100644 src/TheOpenNetwork/Entry.cpp create mode 100644 src/TheOpenNetwork/Entry.h create mode 100644 src/TheOpenNetwork/Message.cpp create mode 100644 src/TheOpenNetwork/Message.h create mode 100644 src/TheOpenNetwork/Signer.cpp create mode 100644 src/TheOpenNetwork/Signer.h create mode 100644 src/TheOpenNetwork/WorkchainType.h create mode 100644 src/TheOpenNetwork/wallet/Wallet.cpp create mode 100644 src/TheOpenNetwork/wallet/Wallet.h create mode 100644 src/TheOpenNetwork/wallet/WalletV4R2.cpp create mode 100644 src/TheOpenNetwork/wallet/WalletV4R2.h create mode 100644 src/proto/TheOpenNetwork.proto create mode 100644 swift/Tests/Blockchains/TheOpenNetworkTests.swift create mode 100644 tests/chains/TheOpenNetwork/AddressTests.cpp create mode 100644 tests/chains/TheOpenNetwork/SignerTests.cpp create mode 100644 tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp create mode 100644 tests/chains/TheOpenNetwork/TWAnySignerTests.cpp create mode 100644 tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp create mode 100644 wasm/tests/Blockchain/TheOpenNetwork.test.ts 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 4e49c45c43e..d1eec662f19 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 @@ -105,6 +105,7 @@ class CoinAddressDerivationTests { NATIVEEVMOS -> assertEquals("evmos13u6g7vqgw074mgmf2ze2cadzvkz9snlwstd20d", address) NERVOS -> assertEquals("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3", address) EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) + TON -> assertEquals("EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9", address) APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) HEDERA -> assertEquals("0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5", address) SECRET -> assertEquals("secret1f69sk5033zcdr2p2yf3xjehn7xvgdeq09d2llh", address) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt new file mode 100644 index 00000000000..d2f341dc62f --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt @@ -0,0 +1,50 @@ +// Copyright © 2017-2022 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.theopennetwork + +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 TestTheOpenNetworkAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddressFromPrivateKey() { + val privateKey = PrivateKey("63474e5fe9511f1526a50567ce142befc343e71a49b865ac3908f58667319cb8".toHexByteArray()) + val publicKey = privateKey.getPublicKeyEd25519() + val address = AnyAddress(publicKey, CoinType.TON) + assertEquals(publicKey.data().toHex(), "0xf42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41") + assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + } + + @Test + fun testAddressFromPublicKey() { + val publicKey = PublicKey("f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41".toHexByteArray(), PublicKeyType.ED25519) + val address = AnyAddress(publicKey, CoinType.TON) + assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + } + + @Test + fun testAddressFromRawString() { + val addressString = "0:66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3" + val address = AnyAddress(addressString, CoinType.TON) + assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + } + + @Test + fun testAddressFromUserFriendlyString() { + val addressString = "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q" + val address = AnyAddress(addressString, CoinType.TON) + assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt new file mode 100644 index 00000000000..fcc1336c954 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt @@ -0,0 +1,50 @@ +// Copyright © 2017-2022 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.theopennetwork + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.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.TheOpenNetwork +import wallet.core.jni.proto.TheOpenNetwork.SigningOutput + +class TestTheOpenNetworkSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun TheOpenNetworkTransactionSigning() { + val privateKey = PrivateKey("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0".toHexByteArray()) + + val transfer = TheOpenNetwork.Transfer.newBuilder() + .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2) + .setDest("EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + .setAmount(10) + .setSequenceNumber(6) + .setMode(TheOpenNetwork.SendMode.PAY_FEES_SEPARATELY_VALUE or TheOpenNetwork.SendMode.IGNORE_ACTION_PHASE_ERRORS_VALUE) + .setExpireAt(1671132440) + .build() + + val input = TheOpenNetwork.SigningInput.newBuilder() + .setTransfer(transfer) + .setPrivateKey(ByteString.copyFrom(privateKey.data())) + .build() + + val output = AnySigner.sign(input, CoinType.TON, SigningOutput.parser()) + + // tx: https://tonscan.org/tx/3Z4tHpXNLyprecgu5aTQHWtY7dpHXEoo11MAX61Xyg0= + val expectedString = "te6ccgICAAQAAQAAALAAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcEUPkil2aZ4s8KKparSep/OKHMC8vuXafFbW2HGp/9AcTRv0J5T4dwyW1G0JpHw+g5Ov6QI3Xo0O9RFr3KidICimpoxdjm3UYAAAABgADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwAA" + + assertEquals(output.encoded, expectedString) + } +} diff --git a/docs/registry.md b/docs/registry.md index 823c1526609..51cb097e664 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -52,6 +52,7 @@ This list is generated from [./registry.json](../registry.json) | 508 | MultiversX | eGLD | | | | 529 | Secret | SCRT | | | | 564 | Agoric | BLD | | | +| 607 | TON | TON | | | | 637 | Aptos | APT | | | | 714 | BNB Beacon Chain | BNB | | | | 818 | VeChain | VET | | | diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 44724e534f6..4231010137d 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -56,6 +56,7 @@ enum TWBlockchain { TWBlockchainEverscale = 42, TWBlockchainAptos = 43, // Aptos TWBlockchainHedera = 44, // Hedera + TWBlockchainTheOpenNetwork = 45, }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 6bd2e78b158..661ccebf68d 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -128,6 +128,7 @@ enum TWCoinType { TWCoinTypeSecret = 529, TWCoinTypeNativeInjective = 10000060, TWCoinTypeAgoric = 564, + TWCoinTypeTON = 607, }; /// Returns the blockchain for a coin type. diff --git a/registry.json b/registry.json index 8bc0bedea72..81df647a241 100644 --- a/registry.json +++ b/registry.json @@ -2942,5 +2942,33 @@ "url": "https://injective.com", "documentation": "https://docs.injective.network" } + }, + { + "id": "ton", + "name": "TON", + "coinId": 607, + "symbol": "TON", + "decimals": 9, + "blockchain": "The Open Network", + "derivation": [ + { + "path": "m/44'/607'/0'" + } + ], + "curve": "ed25519", + "publicKeyType": "ed25519", + "explorer": { + "url": "https://tonscan.org", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "fJXfn0EVhV09HFuEgUHu4Cchb24nUQtIMwSzmzk2tLs=", + "sampleAccount": "EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N" + }, + "info": { + "url": "https://ton.org", + "source": "https://github.com/ton-blockchain", + "rpc": "https://toncenter.com/api/v2/jsonRPC", + "documentation": "https://ton.org/docs" + } } ] diff --git a/src/Base64.cpp b/src/Base64.cpp index cf4605592ad..edb5db03998 100644 --- a/src/Base64.cpp +++ b/src/Base64.cpp @@ -16,6 +16,26 @@ namespace TW::Base64 { using namespace TW; using namespace std; +static bool isBase64Any(const string& val, const char* alphabet) { + if (val.length() % 4 != 0) { + return false; + } + size_t first_non_alphabet = val.find_first_not_of(alphabet); + size_t first_non_padding = val.find_first_not_of("=", first_non_alphabet); + + if (first_non_alphabet == std::string::npos || + (first_non_padding == std::string::npos && (val.length() - first_non_alphabet < 3))) { + return true; + } + return false; +} + +bool isBase64orBase64Url(const string& val) { + const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + const char* base64_url_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return isBase64Any(val, base64_chars) || isBase64Any(val, base64_url_chars); +} + Data decode(const string& val) { using namespace boost::archive::iterators; using It = transform_width, 8, 6>; diff --git a/src/Base64.h b/src/Base64.h index 5bc20e6fc70..8ea6f74356b 100644 --- a/src/Base64.h +++ b/src/Base64.h @@ -10,6 +10,9 @@ namespace TW::Base64 { +// Checks if as string is in Base64-format or Base64Url-format +bool isBase64orBase64Url(const std::string& val); + // Decode a Base64-format string Data decode(const std::string& val); diff --git a/src/Coin.cpp b/src/Coin.cpp index dd82fc790d9..521db9fb0a8 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -56,6 +56,7 @@ #include "Zcash/Entry.h" #include "Zilliqa/Entry.h" #include "Hedera/Entry.h" +#include "TheOpenNetwork/Entry.h" // end_of_coin_includes_marker_do_not_modify using namespace TW; @@ -105,6 +106,7 @@ Zilliqa::Entry zilliqaDP; Nervos::Entry NervosDP; Everscale::Entry EverscaleDP; Hedera::Entry HederaDP; +TheOpenNetwork::Entry tonDP; // end_of_coin_dipatcher_declarations_marker_do_not_modify CoinEntry* coinDispatcher(TWCoinType coinType) { @@ -156,6 +158,7 @@ CoinEntry* coinDispatcher(TWCoinType coinType) { case TWBlockchainEverscale: entry = &EverscaleDP; break; case TWBlockchainAptos: entry = &AptosDP; break; case TWBlockchainHedera: entry = &HederaDP; break; + case TWBlockchainTheOpenNetwork: entry = &tonDP; break; // end_of_coin_dipatcher_switch_marker_do_not_modify default: entry = nullptr; break; diff --git a/src/Everscale/Address.cpp b/src/Everscale/Address.cpp index 177313473cd..c59331e110e 100644 --- a/src/Everscale/Address.cpp +++ b/src/Everscale/Address.cpp @@ -10,45 +10,15 @@ #include "Address.h" #include "HexCoding.h" #include "Wallet.h" -#include "WorkchainType.h" using namespace TW; namespace TW::Everscale { -using MaybeWorkchain = std::optional>; - -MaybeWorkchain parseWorkchainId(const std::string& string) { - if (auto pos = string.find(':'); pos != std::string::npos) { - try { - auto workchainId = static_cast(std::stoi(string.substr(0, pos))); - return std::make_pair(workchainId, pos + 1); - } catch (...) { - // Do nothing and return empty value later - } - } - - return {}; -} +using AddressImpl = TW::CommonTON::RawAddress; bool Address::isValid(const std::string& string) noexcept { - auto parsed = parseWorkchainId(string); - if (!parsed.has_value()) { - return false; - } - - auto [workchainId, pos] = *parsed; - - if (workchainId != WorkchainType::Basechain && workchainId != WorkchainType::Masterchain) { - return false; - } - - if (string.size() != pos + hexAddrLen) { - return false; - } - - std::string addr = string.substr(pos); - return parse_hex(addr).size() == size; + return AddressImpl::isValid(string); } Address::Address(const std::string& string) { @@ -56,13 +26,7 @@ Address::Address(const std::string& string) { throw std::invalid_argument("Invalid address string!"); } - auto parsed = parseWorkchainId(string); - auto [parsedWorkchainId, pos] = *parsed; - - workchainId = parsedWorkchainId; - - const auto parsedHash = parse_hex(string.substr(pos)); - std::copy(begin(parsedHash), end(parsedHash), begin(hash)); + addressData = AddressImpl::splitAddress(string); } Address::Address(const PublicKey& publicKey, int8_t workchainId) @@ -70,8 +34,7 @@ Address::Address(const PublicKey& publicKey, int8_t workchainId) } std::string Address::string() const { - std::string address = std::to_string(workchainId) + ":" + hex(hash); - return address; + return AddressImpl::to_string(addressData); } } // namespace TW::Everscale diff --git a/src/Everscale/Address.h b/src/Everscale/Address.h index d97ec61f44c..9f76f64e2e4 100644 --- a/src/Everscale/Address.h +++ b/src/Everscale/Address.h @@ -7,25 +7,20 @@ #pragma once #include "Data.h" -#include "../PublicKey.h" +#include "PublicKey.h" + +#include "CommonTON/RawAddress.h" #include #include namespace TW::Everscale { +using AddressData = CommonTON::AddressData; + class Address { public: - /// Number of bytes in an address - static const size_t size = Hash::sha256Size; - - /// Hex address length - static const size_t hexAddrLen = size * 2; - - /// Workchain ID (-1 for masterchain, 0 for base workchain) - std::int8_t workchainId; - /// StateInit hash - std::array hash{}; + AddressData addressData; /// Determines whether a string makes a valid address. [[nodiscard]] static bool isValid(const std::string& string) noexcept; @@ -37,15 +32,19 @@ class Address { explicit Address(const PublicKey& publicKey, int8_t workchainId); /// Initializes an Everscale address with its parts - explicit Address(int8_t workchainId, std::array hash) - : workchainId(workchainId), hash(hash) {} + explicit Address(int8_t workchainId, std::array hash) + : addressData(workchainId, hash) {} + + /// Initializes an Everscale address with AddressData + explicit Address(AddressData addressData) + : addressData(addressData) {} /// Returns a string representation of the address. [[nodiscard]] std::string string() const; }; inline bool operator==(const Address& lhs, const Address& rhs) { - return lhs.workchainId == rhs.workchainId && lhs.hash == rhs.hash; + return lhs.addressData.workchainId == rhs.addressData.workchainId && lhs.addressData.hash == rhs.addressData.hash; } } // namespace TW::Everscale diff --git a/src/Everscale/Cell.cpp b/src/Everscale/CommonTON/Cell.cpp similarity index 99% rename from src/Everscale/Cell.cpp rename to src/Everscale/CommonTON/Cell.cpp index 41d9ec32a81..eebd31a5063 100644 --- a/src/Everscale/Cell.cpp +++ b/src/Everscale/CommonTON/Cell.cpp @@ -11,14 +11,14 @@ #include #include -#include "../BinaryCoding.h" +#include "BinaryCoding.h" #include #include using namespace TW; -namespace TW::Everscale { +namespace TW::CommonTON { constexpr static uint32_t BOC_MAGIC = 0xb5ee9c72; @@ -397,4 +397,4 @@ void Cell::finalize() { finalized = true; } -} // namespace TW::Everscale +} // namespace TW::CommonTON diff --git a/src/Everscale/Cell.h b/src/Everscale/CommonTON/Cell.h similarity index 96% rename from src/Everscale/Cell.h rename to src/Everscale/CommonTON/Cell.h index 0a621ffcf19..56240993172 100644 --- a/src/Everscale/Cell.h +++ b/src/Everscale/CommonTON/Cell.h @@ -13,9 +13,9 @@ #include #include "Data.h" -#include "../Hash.h" +#include "Hash.h" -namespace TW::Everscale { +namespace TW::CommonTON { class Cell { public: @@ -63,4 +63,4 @@ class Cell { } }; -} // namespace TW::Everscale +} // namespace TW::CommonTON diff --git a/src/Everscale/CellBuilder.cpp b/src/Everscale/CommonTON/CellBuilder.cpp similarity index 98% rename from src/Everscale/CellBuilder.cpp rename to src/Everscale/CommonTON/CellBuilder.cpp index 8ddc3ccbc94..d077b7098ea 100644 --- a/src/Everscale/CellBuilder.cpp +++ b/src/Everscale/CommonTON/CellBuilder.cpp @@ -11,11 +11,11 @@ #include #include -#include "../BinaryCoding.h" +#include "BinaryCoding.h" using namespace TW; -namespace TW::Everscale { +namespace TW::CommonTON { CellBuilder::CellBuilder(Data& appendedData, uint16_t bits) { assert(bits <= appendedData.size() * 8); @@ -257,4 +257,4 @@ void CellBuilder::encode128BE(const uint128_t& val, Data& data) { data.emplace_back(static_cast(val)); } -} // namespace TW::Everscale +} // namespace TW::CommonTON diff --git a/src/Everscale/CellBuilder.h b/src/Everscale/CommonTON/CellBuilder.h similarity index 96% rename from src/Everscale/CellBuilder.h rename to src/Everscale/CommonTON/CellBuilder.h index 8b192ec42b3..596393c7ae5 100644 --- a/src/Everscale/CellBuilder.h +++ b/src/Everscale/CommonTON/CellBuilder.h @@ -13,7 +13,7 @@ #include "Cell.h" #include "CellSlice.h" -namespace TW::Everscale { +namespace TW::CommonTON { class CellBuilder { uint16_t bitLen = 0; @@ -52,4 +52,4 @@ class CellBuilder { static void encode128BE(const uint128_t& value, Data& data); }; -} // namespace TW::Everscale +} // namespace TW::CommonTON diff --git a/src/Everscale/CellSlice.cpp b/src/Everscale/CommonTON/CellSlice.cpp similarity index 94% rename from src/Everscale/CellSlice.cpp rename to src/Everscale/CommonTON/CellSlice.cpp index 346f4a65365..68e5f2af433 100644 --- a/src/Everscale/CellSlice.cpp +++ b/src/Everscale/CommonTON/CellSlice.cpp @@ -8,11 +8,11 @@ #include -#include "../BinaryCoding.h" +#include "BinaryCoding.h" using namespace TW; -namespace TW::Everscale { +namespace TW::CommonTON { uint32_t CellSlice::getNextU32() { const auto bytes = getNextBytes(sizeof(uint32_t)); @@ -56,4 +56,4 @@ void CellSlice::require(uint16_t bits) const { } } -} // namespace TW::Everscale +} // namespace TW::CommonTON diff --git a/src/Everscale/CellSlice.h b/src/Everscale/CommonTON/CellSlice.h similarity index 92% rename from src/Everscale/CellSlice.h rename to src/Everscale/CommonTON/CellSlice.h index 38c4598b9a5..b553c6ab670 100644 --- a/src/Everscale/CellSlice.h +++ b/src/Everscale/CommonTON/CellSlice.h @@ -12,7 +12,7 @@ #include "Cell.h" -namespace TW::Everscale { +namespace TW::CommonTON { class CellSlice { public: @@ -29,4 +29,4 @@ class CellSlice { void require(uint16_t bits) const; }; -} // namespace TW::Everscale +} // namespace TW::CommonTON diff --git a/src/Everscale/CommonTON/Messages.cpp b/src/Everscale/CommonTON/Messages.cpp new file mode 100644 index 00000000000..ae79a75111f --- /dev/null +++ b/src/Everscale/CommonTON/Messages.cpp @@ -0,0 +1,76 @@ +#include "Messages.h" + +namespace TW::CommonTON { + +void ExternalInboundMessageHeader::writeTo(CellBuilder& builder) const { + builder.appendBitOne(); + builder.appendBitZero(); + + // addr src (none) + builder.appendRaw(Data{0x00}, 2); + + // addr dst + Data dstAddr(_dst.hash.begin(), _dst.hash.end()); + + Data prefix{0x80}; + builder.appendRaw(prefix, 2); + + builder.appendBitZero(); + builder.appendI8(_dst.workchainId); + builder.appendRaw(dstAddr, 256); + + // fee + builder.appendU128(_importFee); +} + +void InternalMessageHeader::writeTo(CellBuilder& builder) const { + // tag + builder.appendBitZero(); + + builder.appendBitBool(_ihrDisabled); + builder.appendBitBool(_bounce); + builder.appendBitBool(_bounced); + + // addr src (none) + builder.appendRaw(Data{0x00}, 2); + + // addr dst + Data dstAddr(_dst.hash.begin(), _dst.hash.end()); + + Data prefix{0x80}; + builder.appendRaw(prefix, 2); + + builder.appendBitZero(); + builder.appendI8(_dst.workchainId); + builder.appendRaw(dstAddr, 256); + + // value + builder.appendU128(_value); + + // currency collections + builder.appendBitZero(); + + // fee + builder.appendU128(_ihrFee); + builder.appendU128(_fwdFee); + + // created + builder.appendU64(_createdLt); + builder.appendU32(_createdAt); +} + +CellBuilder StateInit::writeTo() const { + CellBuilder builder; + + builder.appendBitZero(); // split_depth + builder.appendBitZero(); // special + builder.appendBitOne(); // code + builder.appendReferenceCell(code); + builder.appendBitOne(); // data + builder.appendReferenceCell(data); + builder.appendBitZero(); // library + + return builder; +} + +} // namespace TW::CommonTON \ No newline at end of file diff --git a/src/Everscale/CommonTON/Messages.h b/src/Everscale/CommonTON/Messages.h new file mode 100644 index 00000000000..bd96fe7b731 --- /dev/null +++ b/src/Everscale/CommonTON/Messages.h @@ -0,0 +1,78 @@ +// Copyright © 2017-2022 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 +#include + +#include "Cell.h" +#include "CellBuilder.h" +#include "CellSlice.h" +#include "RawAddress.h" + +#include "PrivateKey.h" + +namespace TW::CommonTON { + +using uint128_t = CellBuilder::uint128_t; + +class CommonMsgInfo { +public: + virtual void writeTo(CellBuilder& builder) const = 0; + + virtual ~CommonMsgInfo() = default; +}; + +class ExternalInboundMessageHeader : public CommonMsgInfo { + AddressData _dst; + uint128_t _importFee{}; + +public: + explicit ExternalInboundMessageHeader(AddressData dst) + : _dst(dst) {} + + void writeTo(CellBuilder& builder) const override; +}; + +class InternalMessageHeader : public CommonMsgInfo { + bool _ihrDisabled; + bool _bounce; + AddressData _dst; + uint128_t _value; + + bool _bounced{}; + uint128_t _ihrFee{}; + uint128_t _fwdFee{}; + uint64_t _createdLt{}; + uint32_t _createdAt{}; + +public: + InternalMessageHeader(bool ihrDisabled, bool bounce, AddressData dst, uint64_t value) + : _ihrDisabled(ihrDisabled), _bounce(bounce), _dst(dst), _value(static_cast(value)) {} + + void writeTo(CellBuilder& builder) const override; +}; + +class StateInit { +public: + Cell::Ref code; + Cell::Ref data; + + [[nodiscard]] CellBuilder writeTo() const; +}; + +struct MessageData { + std::shared_ptr header; + std::optional init{}; + std::optional body{}; + + using HeaderRef = std::shared_ptr; + + explicit MessageData(HeaderRef header) : header(std::move(header)) { } +}; + +} // namespace TW::CommonTON diff --git a/src/Everscale/CommonTON/RawAddress.cpp b/src/Everscale/CommonTON/RawAddress.cpp new file mode 100644 index 00000000000..5aa6199062f --- /dev/null +++ b/src/Everscale/CommonTON/RawAddress.cpp @@ -0,0 +1,103 @@ +// Copyright © 2017-2022 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 "RawAddress.h" + +#include "HexCoding.h" +#include "WorkchainType.h" + +using MaybeWorkchain = std::optional>; + +namespace TW::CommonTON { + +static std::optional parse_long(char const *s) +{ + char *end; + auto previous_errno = errno; + errno = 0; + long l = strtol(s, &end, 10); + if (errno == ERANGE && l == LONG_MAX) { + errno = previous_errno; + return {}; // Overflow + } + if (errno == ERANGE && l == LONG_MIN) { + errno = previous_errno; + return {}; // Underflow + } + if (*s == '\0' || *end != '\0') { + errno = previous_errno; + return {}; // Inconvertible + } + errno = previous_errno; + return l; +} + +static std::optional parse_int8(char const *s) { + auto value = parse_long(s); + if (!value.has_value()) { + return std::nullopt; + } + if (value.value() <= static_cast(std::numeric_limits::max()) && value.value() >= static_cast(std::numeric_limits::min())) { + return value; + } else { + return std::nullopt; + } +} + +static MaybeWorkchain parseWorkchainId(const std::string& string) { + if (auto pos = string.find(':'); pos != std::string::npos) { + std::string workchain_string = string.substr(0, pos); + auto workchain_id = parse_int8(workchain_string.c_str()); + if (workchain_id.has_value()) { + return std::make_pair(workchain_id.value(), pos + 1); + } + } + return {}; +} + +bool RawAddress::isValid(const std::string& string) { + auto parsed = parseWorkchainId(string); + if (!parsed.has_value()) { + return false; + } + + auto [workchainId, pos] = *parsed; + + if (workchainId != WorkchainType::Basechain && workchainId != WorkchainType::Masterchain) { + return false; + } + + if (string.size() != pos + AddressData::hexAddrLen) { + return false; + } + + std::string addr = string.substr(pos); + if (!is_hex_encoded(addr)) { + return false; + } + return parse_hex(addr).size() == AddressData::size; +} + +AddressData RawAddress::splitAddress(const std::string& address) { + auto parsed = parseWorkchainId(address); + auto [parsedWorkchainId, pos] = *parsed; + + auto workchainId = parsedWorkchainId; + + const auto parsedHash = parse_hex(address.substr(pos)); + + std::array hash{}; + std::copy(begin(parsedHash), end(parsedHash), begin(hash)); + + return AddressData(workchainId, hash); +} + +std::string RawAddress::to_string(const AddressData& addressData) { + std::string address = std::to_string(addressData.workchainId) + ":" + hex(addressData.hash); + return address; +} + +} // namespace TW::CommonTON diff --git a/src/Everscale/CommonTON/RawAddress.h b/src/Everscale/CommonTON/RawAddress.h new file mode 100644 index 00000000000..dbca44b11c0 --- /dev/null +++ b/src/Everscale/CommonTON/RawAddress.h @@ -0,0 +1,42 @@ +// Copyright © 2017-2022 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 +#include +#include + +#include "Hash.h" + +namespace TW::CommonTON { + +struct AddressData { + /// Number of bytes in an address + static const size_t size = Hash::sha256Size; + + /// Hex address length + static const size_t hexAddrLen = size * 2; + + /// Workchain ID (-1 for masterchain, 0 for base workchain) + std::int8_t workchainId{}; + + /// StateInit hash + std::array hash{}; + + explicit AddressData(int8_t workchainId, std::array hash) + : workchainId(workchainId), hash(hash) {} + + explicit AddressData() = default; +}; + +struct RawAddress { + static bool isValid(const std::string& string); + static AddressData splitAddress(const std::string& address); + static std::string to_string(const AddressData& addressData); +}; + +} // namespace TW::CommonTON diff --git a/src/Everscale/CommonTON/WorkchainType.h b/src/Everscale/CommonTON/WorkchainType.h new file mode 100644 index 00000000000..9e1c3fee27e --- /dev/null +++ b/src/Everscale/CommonTON/WorkchainType.h @@ -0,0 +1,16 @@ +// Copyright © 2017-2022 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 + +namespace TW::CommonTON { + +enum WorkchainType { + Masterchain = -1, + Basechain = 0, +}; + +} // namespace TW::CommonTON diff --git a/src/Everscale/Messages.cpp b/src/Everscale/Messages.cpp index a20ee263f08..43f48539446 100644 --- a/src/Everscale/Messages.cpp +++ b/src/Everscale/Messages.cpp @@ -5,76 +5,23 @@ // file LICENSE at the root of the source code distribution tree. #include "Messages.h" + +#include "Wallet.h" #include "WorkchainType.h" using namespace TW; namespace TW::Everscale { -void ExternalInboundMessageHeader::writeTo(CellBuilder& builder) const { - builder.appendBitOne(); - builder.appendBitZero(); - - // addr src (none) - builder.appendRaw(Data{0x00}, 2); - - // addr dst - Data dstAddr(_dst.hash.begin(), _dst.hash.end()); - - Data prefix{0x80}; - builder.appendRaw(prefix, 2); - - builder.appendBitZero(); - builder.appendI8(_dst.workchainId); - builder.appendRaw(dstAddr, 256); - - // fee - builder.appendU128(_importFee); -} - -void InternalMessageHeader::writeTo(CellBuilder& builder) const { - // tag - builder.appendBitZero(); - - builder.appendBitBool(_ihrDisabled); - builder.appendBitBool(_bounce); - builder.appendBitBool(_bounced); - - // addr src (none) - builder.appendRaw(Data{0x00}, 2); - - // addr dst - Data dstAddr(_dst.hash.begin(), _dst.hash.end()); - - Data prefix{0x80}; - builder.appendRaw(prefix, 2); - - builder.appendBitZero(); - builder.appendI8(_dst.workchainId); - builder.appendRaw(dstAddr, 256); - - // value - builder.appendU128(_value); - builder.appendBitZero(); - - // fee - builder.appendU128(_ihrFee); - builder.appendU128(_fwdFee); - - // created - builder.appendU64(_createdLt); - builder.appendU32(_createdAt); -} - Cell::Ref Message::intoCell() const { CellBuilder builder; // write Header - _header->writeTo(builder); + _messageData.header->writeTo(builder); // write StateInit - if (_init.has_value()) { - auto initBuilder = _init.value().writeTo(); + if (_messageData.init.has_value()) { + auto initBuilder = _messageData.init.value().writeTo(); builder.appendBitOne(); builder.appendBitZero(); @@ -84,9 +31,9 @@ Cell::Ref Message::intoCell() const { } // write Body - if (_body.has_value()) { + if (_messageData.body.has_value()) { builder.appendBitZero(); - builder.appendCellSlice(_body.value()); + builder.appendCellSlice(CellSlice(_messageData.body.value().get())); } else { builder.appendBitZero(); } @@ -94,8 +41,8 @@ Cell::Ref Message::intoCell() const { return builder.intoCell(); } -Data createSignedMessage(PublicKey& publicKey, PrivateKey& key, bool bounce, uint32_t flags, uint64_t amount, uint32_t expiredAt, - Address to, const Cell::Ref& contractData) { +MessageData createSignedMessage(PublicKey& publicKey, PrivateKey& key, bool bounce, uint32_t flags, uint64_t amount, uint32_t expiredAt, + Address to, const Cell::Ref& contractData) { auto getInitData = [](const PublicKey& publicKey, const Cell::Ref& contractData) { if (contractData != nullptr) { auto cellSlice = CellSlice(contractData.get()); @@ -124,23 +71,15 @@ Data createSignedMessage(PublicKey& publicKey, PrivateKey& key, bool bounce, uin payload.prependRaw(signature, static_cast(signature.size()) * 8); auto header = std::make_shared(InitData(publicKey).computeAddr(WorkchainType::Basechain)); - auto message = Message(header); + auto message = MessageData(header); if (withInitState) { - message.setStateInit(initData.makeStateInit()); + message.init = initData.makeStateInit(); } - auto cell = payload.intoCell(); - auto body = CellSlice(cell.get()); - - message.setBody(body); - - const auto messageCell = message.intoCell(); - - Data result{}; - messageCell->serialize(result); + message.body = payload.intoCell(); - return result; + return message; } } // namespace TW::Everscale diff --git a/src/Everscale/Messages.h b/src/Everscale/Messages.h index 345f75667ec..c0eb9ca027c 100644 --- a/src/Everscale/Messages.h +++ b/src/Everscale/Messages.h @@ -6,76 +6,23 @@ #pragma once -#include -#include - #include "Address.h" -#include "CellBuilder.h" -#include "CellSlice.h" -#include "Wallet.h" +#include "CommonTON/Messages.h" -#include "../PrivateKey.h" +using namespace TW::CommonTON; namespace TW::Everscale { -using uint128_t = CellBuilder::uint128_t; - -class CommonMsgInfo { -public: - virtual void writeTo(CellBuilder& builder) const = 0; - - virtual ~CommonMsgInfo() = default; -}; - -class ExternalInboundMessageHeader : public CommonMsgInfo { - Address _dst; - uint128_t _importFee{}; - -public: - explicit ExternalInboundMessageHeader(Address dst) - : _dst(dst) {} - - void writeTo(CellBuilder& builder) const override; -}; - -class InternalMessageHeader : public CommonMsgInfo { - bool _ihrDisabled; - bool _bounce; - Address _dst; - uint128_t _value; - - bool _bounced{}; - uint128_t _ihrFee{}; - uint128_t _fwdFee{}; - uint64_t _createdLt{}; - uint32_t _createdAt{}; - -public: - InternalMessageHeader(bool ihrDisabled, bool bounce, Address dst, uint64_t value) - : _ihrDisabled(ihrDisabled), _bounce(bounce), _dst(dst), _value(static_cast(value)) {} - - void writeTo(CellBuilder& builder) const override; -}; - class Message { private: - std::shared_ptr _header; - - std::optional _init{}; - std::optional _body{}; + MessageData _messageData; public: - using HeaderRef = std::shared_ptr; - - explicit Message(HeaderRef header) - : _header(std::move(header)) {} - + explicit Message(MessageData messageData) : _messageData(std::move(messageData)) {} [[nodiscard]] Cell::Ref intoCell() const; - inline void setBody(CellSlice body) { _body = body; } - inline void setStateInit(const StateInit& stateInit) { _init = stateInit; } }; -Data createSignedMessage(PublicKey& publicKey, PrivateKey& key, bool bounce, uint32_t flags, uint64_t amount, - uint32_t expiredAt, Address destination, const Cell::Ref& contractData); +MessageData createSignedMessage(PublicKey& publicKey, PrivateKey& key, bool bounce, uint32_t flags, uint64_t amount, + uint32_t expiredAt, Address destination, const Cell::Ref& contractData); } // namespace TW::Everscale diff --git a/src/Everscale/Signer.cpp b/src/Everscale/Signer.cpp index 1f2a0fb9d10..a76bd35f690 100644 --- a/src/Everscale/Signer.cpp +++ b/src/Everscale/Signer.cpp @@ -7,11 +7,9 @@ #include "Signer.h" #include "Address.h" #include "Messages.h" +#include "Wallet.h" -#include "../Base64.h" - -using namespace TW; -using namespace std::chrono; +#include "Base64.h" namespace TW::Everscale { @@ -56,7 +54,10 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { transfer.expired_at(), Address(transfer.to()), contractData); - protoOutput.set_encoded(TW::Base64::encode(signedMessage)); + Data result{}; + Message(signedMessage).intoCell()->serialize(result); + + protoOutput.set_encoded(TW::Base64::encode(result)); break; } default: diff --git a/src/Everscale/Wallet.cpp b/src/Everscale/Wallet.cpp index 09bf1e442e5..0ea085209d2 100644 --- a/src/Everscale/Wallet.cpp +++ b/src/Everscale/Wallet.cpp @@ -5,13 +5,10 @@ // file LICENSE at the root of the source code distribution tree. #include "Wallet.h" -#include "CellBuilder.h" #include "Messages.h" #include "HexCoding.h" -using namespace TW; - namespace TW::Everscale { // WalletV3 contract https://github.com/tonlabs/ton-1/blob/master/crypto/smartcont/wallet3-code.fc @@ -27,14 +24,14 @@ CellBuilder InitData::writeTo() const { return builder; } -Address InitData::computeAddr(int8_t workchainId) const { +AddressData InitData::computeAddr(int8_t workchainId) const { auto builder = this->writeTo(); StateInit stateInit{ .code = Cell::deserialize(Wallet::code.data(), Wallet::code.size()), .data = builder.intoCell(), }; - return Address(workchainId, stateInit.writeTo().intoCell()->hash); + return AddressData(workchainId, stateInit.writeTo().intoCell()->hash); } StateInit InitData::makeStateInit() const { @@ -55,8 +52,8 @@ CellBuilder InitData::makeTransferPayload(uint32_t expireAt, const Wallet::Gift& payload.appendU32(_seqno); // create internal message - Message::HeaderRef header = std::make_shared(true, gift.bounce, gift.to, gift.amount); - auto message = Message(header); + MessageData::HeaderRef header = std::make_shared(true, gift.bounce, gift.to.addressData, gift.amount); + auto message = Message(MessageData(header)); // append it to the body payload.appendU8(gift.flags); @@ -65,18 +62,4 @@ CellBuilder InitData::makeTransferPayload(uint32_t expireAt, const Wallet::Gift& return payload; } -CellBuilder StateInit::writeTo() const { - CellBuilder builder; - - builder.appendBitZero(); // split_depth - builder.appendBitZero(); // special - builder.appendBitOne(); // code - builder.appendReferenceCell(code); - builder.appendBitOne(); // data - builder.appendReferenceCell(data); - builder.appendBitZero(); // library - - return builder; -} - } // namespace TW::Everscale diff --git a/src/Everscale/Wallet.h b/src/Everscale/Wallet.h index a0216c465db..93dd7a4f98a 100644 --- a/src/Everscale/Wallet.h +++ b/src/Everscale/Wallet.h @@ -9,15 +9,18 @@ #include #include -#include "../PublicKey.h" +#include "PublicKey.h" #include "Address.h" -#include "Cell.h" -#include "CellBuilder.h" -#include "CellSlice.h" +#include "CommonTON/Cell.h" +#include "CommonTON/CellBuilder.h" +#include "CommonTON/CellSlice.h" +#include "CommonTON/Messages.h" const uint32_t WALLET_ID = 0x4BA92D8A; +using namespace TW::CommonTON; + namespace TW::Everscale { class Wallet { @@ -49,14 +52,6 @@ class Wallet { static const Data code; }; -class StateInit { -public: - Cell::Ref code; - Cell::Ref data; - - [[nodiscard]] CellBuilder writeTo() const; -}; - class InitData { uint32_t _seqno; uint32_t _walletId; @@ -70,7 +65,7 @@ class InitData { [[nodiscard]] CellBuilder writeTo() const; [[nodiscard]] StateInit makeStateInit() const; - [[nodiscard]] Address computeAddr(int8_t workchainId) const; + [[nodiscard]] AddressData computeAddr(int8_t workchainId) const; [[nodiscard]] CellBuilder makeTransferPayload(uint32_t expireAt, const Wallet::Gift& gift) const; }; diff --git a/src/Everscale/WorkchainType.h b/src/Everscale/WorkchainType.h index 786343e1065..cd52b72d15e 100644 --- a/src/Everscale/WorkchainType.h +++ b/src/Everscale/WorkchainType.h @@ -6,11 +6,8 @@ #pragma once -namespace TW::Everscale { - -enum WorkchainType { - Masterchain = -1, - Basechain = 0, -}; +#include "CommonTON/WorkchainType.h" +namespace TW::Everscale { + using WorkchainType = CommonTON::WorkchainType; } // namespace TW::Everscale diff --git a/src/HexCoding.h b/src/HexCoding.h index 8cf121ae3e0..f047065a85b 100644 --- a/src/HexCoding.h +++ b/src/HexCoding.h @@ -17,6 +17,15 @@ namespace TW { +inline bool is_hex_encoded(const std::string& s) +{ + bool with_0x = s.compare(0, 2, "0x") == 0 + && s.size() > 2 + && s.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos; + bool without_0x = s.find_first_not_of("0123456789abcdefABCDEF") == std::string::npos; + return with_0x || without_0x; +} + std::tuple value(uint8_t c); /// Converts a range of bytes to a hexadecimal string representation. diff --git a/src/TheOpenNetwork/Address.cpp b/src/TheOpenNetwork/Address.cpp new file mode 100644 index 00000000000..8f6a3b2b9a3 --- /dev/null +++ b/src/TheOpenNetwork/Address.cpp @@ -0,0 +1,125 @@ +// Copyright © 2017-2022 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 "Base64.h" +#include "Crc.h" + +#include "WorkchainType.h" + +namespace TW::TheOpenNetwork { + +using AddressImpl = TW::CommonTON::RawAddress; + +static inline Data decodeUserFriendlyAddress(const std::string& string) { + Data decoded; + if (string.find('-') != std::string::npos || string.find('_') != std::string::npos) { + decoded = Base64::decodeBase64Url(string); + } else { + decoded = Base64::decode(string); + } + return decoded; +} + +bool Address::isValid(const std::string& string) noexcept { + if (AddressImpl::isValid(string)) { + return true; + } + + if (string.size() != b64UserFriendlyAddressLen) { + return false; + } + + if (!Base64::isBase64orBase64Url(string)) { + return false; + } + Data decoded = decodeUserFriendlyAddress(string); + + if (decoded.size() != userFriendlyAddressLen) { + return false; + } + + byte tag = decoded[0]; + if (tag & AddressTag::TEST_ONLY) { + tag ^= AddressTag::TEST_ONLY; + } + + if (tag != AddressTag::BOUNCEABLE && tag != AddressTag::NON_BOUNCEABLE) { + return false; + } + + int8_t workchainId = decoded[1]; + if (workchainId != WorkchainType::Basechain && workchainId != WorkchainType::Masterchain) { + return false; + } + + Data data(decoded.begin(), decoded.end() - 2); + Data givenCrc(decoded.end() - 2, decoded.end()); + + const uint16_t crc16 = Crc::crc16(data.data(), (uint32_t) data.size()); + const byte b1 = (crc16 >> 8) & 0xff; + const byte b2 = crc16 & 0xff; + if (b1 != givenCrc[0] || b2 != givenCrc[1]) { + return false; + } + + return true; +} + +Address::Address(const std::string& string) { + if (!Address::isValid(string)) { + throw std::invalid_argument("Invalid address string"); + } + + if (string.find(':') != std::string::npos) { + addressData = AddressImpl::splitAddress(string); + } else { + isUserFriendly = true; + + Data decoded = decodeUserFriendlyAddress(string); + addressData.workchainId = decoded[1]; + + byte tag = decoded[0]; + if (tag & AddressTag::TEST_ONLY) { + isTestOnly = true; + tag ^= AddressTag::TEST_ONLY; + } + isBounceable = (tag == AddressTag::BOUNCEABLE); + + std::copy(decoded.begin() + 2, decoded.end() - 2, addressData.hash.begin()); + } +} + +std::string Address::string() const { + return this->string(isUserFriendly, isBounceable, isTestOnly); +} + +std::string Address::string(bool userFriendly, bool bounceable, bool testOnly) const { + if (!userFriendly) { + return AddressImpl::to_string(addressData); + } + + Data data; + Data hashData(addressData.hash.begin(), addressData.hash.end()); + + byte tag = bounceable ? AddressTag::BOUNCEABLE : AddressTag::NON_BOUNCEABLE; + if (testOnly) { + tag |= AddressTag::TEST_ONLY; + } + + append(data, tag); + append(data, addressData.workchainId); + append(data, hashData); + + const uint16_t crc16 = Crc::crc16(data.data(), (uint32_t) data.size()); + append(data, (crc16 >> 8) & 0xff); + append(data, crc16 & 0xff); + + return Base64::encodeBase64Url(data); +} + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/Address.h b/src/TheOpenNetwork/Address.h new file mode 100644 index 00000000000..2175926f381 --- /dev/null +++ b/src/TheOpenNetwork/Address.h @@ -0,0 +1,63 @@ +// Copyright © 2017-2022 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 "Everscale/CommonTON/RawAddress.h" + +#include + +namespace TW::TheOpenNetwork { + +enum AddressTag : uint8_t { + BOUNCEABLE = 0x11, + NON_BOUNCEABLE = 0x51, + TEST_ONLY = 0x80, +}; + +using AddressData = CommonTON::AddressData; + +class Address { +public: + AddressData addressData; + + /// User-friendly address lens + static const size_t b64UserFriendlyAddressLen = 48; + static const size_t userFriendlyAddressLen = 36; + + /// Determines whether the address is user-friendly + bool isUserFriendly = false; + + /// Determines whether the address is bounceable + bool isBounceable = false; + + /// Determines whether the address is for tests only + bool isTestOnly = false; + + /// Determines whether a string makes a valid address. + [[nodiscard]] static bool isValid(const std::string& string) noexcept; + + /// Initializes an address with a string representation. + explicit Address(const std::string& string); + + /// Initializes an address with its parts + explicit Address( + int8_t workchainId, std::array hash, + bool userFriendly = true, bool bounceable = true, bool testOnly = false + ) : addressData(workchainId, hash), + isUserFriendly(userFriendly), + isBounceable(bounceable), + isTestOnly(testOnly) {} + + /// Returns a string representation of the address. + [[nodiscard]] std::string string() const; + [[nodiscard]] std::string string(bool userFriendly, bool bounceable = true, bool testOnly = false) const; +}; + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/Entry.cpp b/src/TheOpenNetwork/Entry.cpp new file mode 100644 index 00000000000..21f2e19de1b --- /dev/null +++ b/src/TheOpenNetwork/Entry.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2022 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" + +#include "TheOpenNetwork/wallet/WalletV4R2.h" +#include "WorkchainType.h" + +namespace TW::TheOpenNetwork { + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return Address::isValid(address); +} + +std::string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const std::string& address) const { + return Address(address).string(true, true, false); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return WalletV4R2(publicKey, WorkchainType::Basechain).getAddress().string(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/Entry.h b/src/TheOpenNetwork/Entry.h new file mode 100644 index 00000000000..a432b493bda --- /dev/null +++ b/src/TheOpenNetwork/Entry.h @@ -0,0 +1,21 @@ +// Copyright © 2017-2022 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::TheOpenNetwork { + +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; +}; + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/Message.cpp b/src/TheOpenNetwork/Message.cpp new file mode 100644 index 00000000000..cf76cb7df72 --- /dev/null +++ b/src/TheOpenNetwork/Message.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 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 "Message.h" + +namespace TW::TheOpenNetwork { + +Cell::Ref Message::intoCell() const { + CellBuilder builder; + + // info:CommonMsgInfo + _messageData.header->writeTo(builder); + + // init:(Maybe (Either StateInit ^StateInit)) + if (_messageData.init.has_value()) { + builder.appendBitOne(); // Maybe + + builder.appendBitOne(); // Either as ^X + builder.appendReferenceCell(_messageData.init.value().writeTo().intoCell()); + } else { + builder.appendBitZero(); + } + + // body:(Either X ^X) + if (_messageData.body.has_value()) { + builder.appendBitOne(); // Either as ^X + builder.appendReferenceCell(_messageData.body.value()); + } else { + builder.appendBitZero(); + } + + return builder.intoCell(); +} + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/Message.h b/src/TheOpenNetwork/Message.h new file mode 100644 index 00000000000..5b7aee9cc84 --- /dev/null +++ b/src/TheOpenNetwork/Message.h @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 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 "Everscale/CommonTON/Messages.h" + +using namespace TW::CommonTON; + +namespace TW::TheOpenNetwork { + +class Message { +private: + MessageData _messageData; + +public: + explicit Message(MessageData messageData) : _messageData(std::move(messageData)) {} + + [[nodiscard]] Cell::Ref intoCell() const; + + inline void setBody(const Cell::Ref& body) { _messageData.body = body; } + inline void setStateInit(const StateInit& stateInit) { _messageData.init = stateInit; } +}; + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/Signer.cpp b/src/TheOpenNetwork/Signer.cpp new file mode 100644 index 00000000000..df5e6db1a27 --- /dev/null +++ b/src/TheOpenNetwork/Signer.cpp @@ -0,0 +1,65 @@ +// Copyright © 2017-2022 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 "Base64.h" + +#include "TheOpenNetwork/wallet/WalletV4R2.h" +#include "WorkchainType.h" + +namespace TW::TheOpenNetwork { + +Data Signer::createTransferMessage(std::shared_ptr wallet, const PrivateKey& privateKey, const Proto::Transfer& transfer) { + const auto msg = wallet->createTransferMessage( + privateKey, + Address(transfer.dest()), + transfer.amount(), + transfer.sequence_number(), + static_cast(transfer.mode()), + transfer.expire_at(), + transfer.comment() + ); + + Data result{}; + msg->serialize(result); + return result; +} + +Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { + const auto& privateKey = PrivateKey(input.private_key()); + const auto& publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + + auto protoOutput = Proto::SigningOutput(); + + switch (input.action_oneof_case()) { + case Proto::SigningInput::ActionOneofCase::kTransfer: { + const auto& transfer = input.transfer(); + + try { + switch (transfer.wallet_version()) { + case Proto::WalletVersion::WALLET_V4_R2: { + const int8_t workchainId = WorkchainType::Basechain; + auto wallet = std::make_shared(publicKey, workchainId); + const auto& transferMessage = Signer::createTransferMessage(wallet, privateKey, transfer); + protoOutput.set_encoded(TW::Base64::encode(transferMessage)); + break; + } + default: + protoOutput.set_error(Common::Proto::Error_invalid_params); + protoOutput.set_error_message("Unsupported wallet version"); + break; + } + } catch (...) { } + break; + } + default: + break; + } + return protoOutput; +} + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/Signer.h b/src/TheOpenNetwork/Signer.h new file mode 100644 index 00000000000..2ca74fa7cb8 --- /dev/null +++ b/src/TheOpenNetwork/Signer.h @@ -0,0 +1,30 @@ +// Copyright © 2017-2022 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 "PrivateKey.h" +#include "wallet/Wallet.h" + +#include "proto/TheOpenNetwork.pb.h" + +namespace TW::TheOpenNetwork { + +/// Helper class that performs TheOpenNetwork transaction signing. +class Signer { +public: + /// Hide default constructor + Signer() = delete; + + /// Creates a signed transfer message + static Data createTransferMessage(std::shared_ptr wallet, const PrivateKey& privateKey, const Proto::Transfer& transfer); + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; +}; + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/WorkchainType.h b/src/TheOpenNetwork/WorkchainType.h new file mode 100644 index 00000000000..9d5e4f43386 --- /dev/null +++ b/src/TheOpenNetwork/WorkchainType.h @@ -0,0 +1,13 @@ +// Copyright © 2017-2022 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 "Everscale/CommonTON/WorkchainType.h" + +namespace TW::TheOpenNetwork { + using WorkchainType = CommonTON::WorkchainType; +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/wallet/Wallet.cpp b/src/TheOpenNetwork/wallet/Wallet.cpp new file mode 100644 index 00000000000..8a381ecf8a3 --- /dev/null +++ b/src/TheOpenNetwork/wallet/Wallet.cpp @@ -0,0 +1,94 @@ +// Copyright © 2017-2022 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 "Wallet.h" + +#include "HexCoding.h" + +namespace TW::TheOpenNetwork { + +static const uint32_t standard_wallet_id = 698983191; + +Wallet::Wallet(PublicKey publicKey, int8_t workchainId, Data walletCode) + : publicKey(std::move(publicKey)) + , workchainId(workchainId) + , walletCode(std::move(walletCode)) + , walletId(standard_wallet_id + workchainId) { +} + +Address Wallet::getAddress() const { + const auto stateInit = this->createStateInit(); + return Address(workchainId, stateInit.writeTo().intoCell()->hash); +} + +CommonTON::StateInit Wallet::createStateInit() const { + Cell::Ref code = Cell::deserialize(walletCode.data(), walletCode.size()); + Cell::Ref data = this->createDataCell(); + return StateInit{code, data}; +} + +Cell::Ref Wallet::createSigningMessage( + const Address& dest, + uint64_t amount, + uint32_t sequence_number, + uint8_t mode, + uint32_t expireAt, + const std::string& comment +) const { + CellBuilder builder; + this->writeSigningPayload(builder, sequence_number, expireAt); + builder.appendU8(mode); + + { // Add internal message as a reference cell + const auto header = std::make_shared(true, dest.isBounceable, dest.addressData, amount); + TheOpenNetwork::Message internalMessage = TheOpenNetwork::Message(MessageData(header)); + + CellBuilder bodyBuilder; + if (!comment.empty()) { + const auto& data = Data(comment.begin(), comment.end()); + bodyBuilder.appendU32(0); + bodyBuilder.appendRaw(data, static_cast(data.size()) * 8); + } + internalMessage.setBody(bodyBuilder.intoCell()); + + builder.appendReferenceCell(internalMessage.intoCell()); + } + + return builder.intoCell(); +} + +Cell::Ref Wallet::createTransferMessage( + const PrivateKey& privateKey, + const Address& dest, + uint64_t amount, + uint32_t sequence_number, + uint8_t mode, + uint32_t expireAt, + const std::string& comment +) const { + const auto transferMessageHeader = std::make_shared(this->getAddress().addressData); + Message transferMessage = Message(MessageData(transferMessageHeader)); + if (sequence_number == 0) { + const auto stateInit = this->createStateInit(); + transferMessage.setStateInit(stateInit); + } + + { // Set body of transfer message + CellBuilder bodyBuilder; + const Cell::Ref signingMessage = this->createSigningMessage(dest, amount, sequence_number, mode, expireAt, comment); + Data data(signingMessage->hash.begin(), signingMessage->hash.end()); + const auto signature = privateKey.sign(data, TWCurveED25519); + + bodyBuilder.appendRaw(signature, static_cast(signature.size()) * 8); + bodyBuilder.appendCellSlice(CellSlice(signingMessage.get())); + + transferMessage.setBody(bodyBuilder.intoCell()); + } + + return transferMessage.intoCell(); +} + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/wallet/Wallet.h b/src/TheOpenNetwork/wallet/Wallet.h new file mode 100644 index 00000000000..8e081363957 --- /dev/null +++ b/src/TheOpenNetwork/wallet/Wallet.h @@ -0,0 +1,59 @@ +// Copyright © 2017-2022 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 "PublicKey.h" +#include "PrivateKey.h" + +#include "TheOpenNetwork/Address.h" +#include "TheOpenNetwork/Message.h" + +#include "Everscale/CommonTON/Messages.h" + +namespace TW::TheOpenNetwork { + +class Wallet { +protected: + PublicKey publicKey; + int8_t workchainId; + + // Explore standard codes: https://github.com/toncenter/tonweb/blob/master/src/contract/wallet/WalletSources.md + const Data walletCode; + const uint32_t walletId; + +public: + explicit Wallet(PublicKey publicKey, int8_t workchainId, Data walletCode); + virtual ~Wallet() noexcept = default; + + [[nodiscard]] Address getAddress() const; + [[nodiscard]] Cell::Ref createTransferMessage( + const PrivateKey& privateKey, + const Address& dest, + uint64_t amount, + uint32_t sequence_number, + uint8_t mode, + uint32_t expireAt = 0, + const std::string& comment = "" + ) const; + +protected: + [[nodiscard]] virtual Cell::Ref createDataCell() const = 0; + virtual void writeSigningPayload(CellBuilder& builder, uint32_t sequence_number = 0, uint32_t expireAt = 0) const = 0; + +private: + [[nodiscard]] Cell::Ref createSigningMessage( + const Address& dest, + uint64_t amount, + uint32_t sequence_number, + uint8_t mode, + uint32_t expireAt = 0, + const std::string& comment = "" + ) const; + [[nodiscard]] CommonTON::StateInit createStateInit() const; +}; + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/wallet/WalletV4R2.cpp b/src/TheOpenNetwork/wallet/WalletV4R2.cpp new file mode 100644 index 00000000000..392129c48d7 --- /dev/null +++ b/src/TheOpenNetwork/wallet/WalletV4R2.cpp @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 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 "WalletV4R2.h" + +#include "HexCoding.h" + +#include + +namespace TW::TheOpenNetwork { + +// WalletV4R2 contract func https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc +// Contract codes https://github.com/toncenter/tonweb/blob/master/src/contract/wallet/WalletSources.md +const Data WalletV4R2::code = parse_hex("B5EE9C72410214010002D4000114FF00F4A413F4BCF2C80B010201200203020148040504F8F28308D71820D31FD31FD31F02F823BBF264ED44D0D31FD31FD3FFF404D15143BAF2A15151BAF2A205F901541064F910F2A3F80024A4C8CB1F5240CB1F5230CBFF5210F400C9ED54F80F01D30721C0009F6C519320D74A96D307D402FB00E830E021C001E30021C002E30001C0039130E30D03A4C8CB1F12CB1FCBFF1011121302E6D001D0D3032171B0925F04E022D749C120925F04E002D31F218210706C7567BD22821064737472BDB0925F05E003FA403020FA4401C8CA07CBFFC9D0ED44D0810140D721F404305C810108F40A6FA131B3925F07E005D33FC8258210706C7567BA923830E30D03821064737472BA925F06E30D06070201200809007801FA00F40430F8276F2230500AA121BEF2E0508210706C7567831EB17080185004CB0526CF1658FA0219F400CB6917CB1F5260CB3F20C98040FB0006008A5004810108F45930ED44D0810140D720C801CF16F400C9ED540172B08E23821064737472831EB17080185005CB055003CF1623FA0213CB6ACB1FCB3FC98040FB00925F03E20201200A0B0059BD242B6F6A2684080A06B90FA0218470D4080847A4937D29910CE6903E9FF9837812801B7810148987159F31840201580C0D0011B8C97ED44D0D70B1F8003DB29DFB513420405035C87D010C00B23281F2FFF274006040423D029BE84C600201200E0F0019ADCE76A26840206B90EB85FFC00019AF1DF6A26840106B90EB858FC0006ED207FA00D4D422F90005C8CA0715CBFFC9D077748018C8CB05CB0222CF165005FA0214CB6B12CCCCC973FB00C84014810108F451F2A7020070810108D718FA00D33FC8542047810108F451F2A782106E6F746570748018C8CB05CB025006CF165004FA0214CB6A12CB1FCB3FC973FB0002006C810108D718FA00D33F305224810108F459F2A782106473747270748018C8CB05CB025005CF165003FA0213CB6ACB1F12CB3FC973FB00000AF400C9ED54696225E5"); + +WalletV4R2::WalletV4R2(PublicKey publicKey, int8_t workchainId) + : Wallet( + std::move(publicKey), + workchainId, + WalletV4R2::code + ) { +} + +Cell::Ref WalletV4R2::createDataCell() const { + CellBuilder builder; + + builder.appendU32(0); // sequence_number + builder.appendU32(walletId); + builder.appendRaw(publicKey.bytes, 256); + builder.appendBitZero(); // no plugins + + return builder.intoCell(); +} + +void WalletV4R2::writeSigningPayload(CellBuilder& builder, uint32_t sequence_number, uint32_t expireAt) const { + builder.appendU32(walletId); + if (sequence_number == 0) { + builder.appendU32(0xffffffff); + } else { + if (expireAt == 0) { + expireAt = (uint32_t) duration_cast( + std::chrono::system_clock::now().time_since_epoch() + ).count() + 60; // TON v4 wallet requires uint32 for now + } + builder.appendU32(expireAt); + } + builder.appendU32(sequence_number); + builder.appendU8(0); +} + +} // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/wallet/WalletV4R2.h b/src/TheOpenNetwork/wallet/WalletV4R2.h new file mode 100644 index 00000000000..82ec5f34959 --- /dev/null +++ b/src/TheOpenNetwork/wallet/WalletV4R2.h @@ -0,0 +1,24 @@ +// Copyright © 2017-2022 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 "Wallet.h" + +namespace TW::TheOpenNetwork { + +class WalletV4R2 : public Wallet { +public: + explicit WalletV4R2(PublicKey publicKey, int8_t workchainId); + + static const Data code; + +private: + [[nodiscard]] Cell::Ref createDataCell() const override; + void writeSigningPayload(CellBuilder& builder, uint32_t sequence_number = 0, uint32_t expireAt = 0) const override; +}; + +} // namespace TW::TheOpenNetwork diff --git a/src/proto/TheOpenNetwork.proto b/src/proto/TheOpenNetwork.proto new file mode 100644 index 00000000000..12d818a828c --- /dev/null +++ b/src/proto/TheOpenNetwork.proto @@ -0,0 +1,75 @@ +// Copyright © 2017-2022 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. + +syntax = "proto3"; + +package TW.TheOpenNetwork.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Common.proto"; + +enum WalletVersion { + WALLET_V3_R1 = 0; + WALLET_V3_R2 = 1; + WALLET_V4_R2 = 2; +}; + +enum SendMode { + DEFAULT = 0; + PAY_FEES_SEPARATELY = 1; + IGNORE_ACTION_PHASE_ERRORS = 2; + DESTROY_ON_ZERO_BALANCE = 32; + ATTACH_ALL_INBOUND_MESSAGE_VALUE = 64; + ATTACH_ALL_CONTRACT_BALANCE = 128; +}; + +message Transfer { + // Wallet version + WalletVersion wallet_version = 1; + + // Recipient address + string dest = 2; + + // Amount to send in nanotons + uint64 amount = 3; + + // Message counter (optional, 0 by default used for the first deploy) + // This field is required, because we need to protect the smart contract against "replay attacks" + // Learn more: https://ton.org/docs/develop/smart-contracts/guidelines/external-messages + uint32 sequence_number = 4; + + // Send mode (optional, 0 by default) + // Learn more: https://ton.org/docs/develop/func/stdlib#send_raw_message + uint32 mode = 5; + + // Expiration UNIX timestamp (optional, now() + 60 by default) + uint32 expire_at = 6; + + // Transfer comment message (optional, empty by default) + string comment = 7; +} + +message SigningInput { + // The secret private key used for signing (32 bytes). + bytes private_key = 1; + + // The payload transfer + oneof action_oneof { + Transfer transfer = 2; + } +} + +// Transaction signing output. +message SigningOutput { + // Signed and base64 encoded BOC message + string encoded = 1; + + // error code, 0 is ok, other codes will be treated as errors + Common.Proto.SigningError error = 2; + + // error code description + string error_message = 3; +} diff --git a/swift/Tests/Blockchains/TheOpenNetworkTests.swift b/swift/Tests/Blockchains/TheOpenNetworkTests.swift new file mode 100644 index 00000000000..506d007fb9d --- /dev/null +++ b/swift/Tests/Blockchains/TheOpenNetworkTests.swift @@ -0,0 +1,62 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class TheOpenNetworkTests: XCTestCase { + func testAddressFromPrivateKey() { + let data = Data(hexString: "63474e5fe9511f1526a50567ce142befc343e71a49b865ac3908f58667319cb8") + let privateKey = PrivateKey(data: data!)! + let publicKey = privateKey.getPublicKeyEd25519() + let address = AnyAddress(publicKey: publicKey, coin: .ton) + XCTAssertEqual(address.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + } + + func testAddressFromPublicKey() { + let data = Data(hexString: "f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41") + let publicKey = PublicKey(data: data!, type: PublicKeyType.ed25519)! + let address = AnyAddress(publicKey: publicKey, coin: .ton) + XCTAssertEqual(address.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + } + + func testAddressFromRawString() { + let addressString = "0:66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3" + let address = AnyAddress(string: addressString, coin: .ton) + XCTAssertEqual(address!.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + } + + func testAddressFromUserFriendlyString() { + let addressString = "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q" + let address = AnyAddress(string: addressString, coin: .ton) + XCTAssertEqual(address!.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + } + + func testSign() { + let privateKeyData = Data(hexString: "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0")! + + let transfer = TheOpenNetworkTransfer.with { + $0.walletVersion = TheOpenNetworkWalletVersion.walletV4R2 + $0.dest = "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q" + $0.amount = 10 + $0.sequenceNumber = 6 + $0.mode = UInt32(TheOpenNetworkSendMode.payFeesSeparately.rawValue | TheOpenNetworkSendMode.ignoreActionPhaseErrors.rawValue) + $0.expireAt = 1671132440 + } + + let input = TheOpenNetworkSigningInput.with { + $0.transfer = transfer + $0.privateKey = privateKeyData + } + + let output: TheOpenNetworkSigningOutput = AnySigner.sign(input: input, coin: .ton) + + // tx: https://tonscan.org/tx/3Z4tHpXNLyprecgu5aTQHWtY7dpHXEoo11MAX61Xyg0= + let expectedString = "te6ccgICAAQAAQAAALAAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcEUPkil2aZ4s8KKparSep/OKHMC8vuXafFbW2HGp/9AcTRv0J5T4dwyW1G0JpHw+g5Ov6QI3Xo0O9RFr3KidICimpoxdjm3UYAAAABgADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwAA" + + XCTAssertEqual(output.encoded, expectedString) + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 6b5a84791be..3d46f67044c 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -253,6 +253,9 @@ class CoinAddressDerivationTests: XCTestCase { case .everscale: let expectedResult = "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .ton: + let expectedResult = "EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9"; + assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .aptos: let expectedResult = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) diff --git a/tests/chains/Everscale/CellBuilderTest.cpp b/tests/chains/Everscale/CellBuilderTest.cpp index 860f3bac6e5..9bc0d05bb2f 100644 --- a/tests/chains/Everscale/CellBuilderTest.cpp +++ b/tests/chains/Everscale/CellBuilderTest.cpp @@ -8,13 +8,12 @@ #include "HexCoding.h" #include "PublicKey.h" -#include "Everscale/Cell.h" -#include "Everscale/CellBuilder.h" +#include "Everscale/CommonTON/Cell.h" +#include "Everscale/CommonTON/CellBuilder.h" #include "Everscale/Wallet.h" #include -using namespace TW; using boost::multiprecision::uint128_t; namespace TW::Everscale { diff --git a/tests/chains/Everscale/CellTests.cpp b/tests/chains/Everscale/CellTests.cpp index c9b1b821cb9..28bd22b81d2 100644 --- a/tests/chains/Everscale/CellTests.cpp +++ b/tests/chains/Everscale/CellTests.cpp @@ -5,16 +5,14 @@ // file LICENSE at the root of the source code distribution tree. #include "Base64.h" -#include "Everscale/Cell.h" -#include "Everscale/Wallet.h" #include "HexCoding.h" -#include -#include + +#include "Everscale/CommonTON/Cell.h" +#include "Everscale/Wallet.h" + #include #include -using namespace TW; - namespace TW::Everscale { // All hashes could be verified using https://ever.bytie.moe/visualizer diff --git a/tests/chains/TheOpenNetwork/AddressTests.cpp b/tests/chains/TheOpenNetwork/AddressTests.cpp new file mode 100644 index 00000000000..8bc16cdb0a0 --- /dev/null +++ b/tests/chains/TheOpenNetwork/AddressTests.cpp @@ -0,0 +1,116 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "PublicKey.h" +#include "PrivateKey.h" + +#include "TheOpenNetwork/Address.h" +#include "TheOpenNetwork/wallet/WalletV4R2.h" +#include "TheOpenNetwork/WorkchainType.h" + +#include + +namespace TW::TheOpenNetwork::tests { + +TEST(TheOpenNetworkAddress, Valid) { + ASSERT_TRUE(Address::isValid("-1:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae")); + ASSERT_TRUE(Address::isValid("0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae")); + + // user-friendly, b64urlsafe + ASSERT_TRUE(Address::isValid("EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl")); + ASSERT_TRUE(Address::isValid("EQBGqFmKe3oY8PChYN9g92ZEV2ybkzVB-hCiesQRn5mFnrNv")); + ASSERT_TRUE(Address::isValid("Ef8JfFPRpHBV_tZpCurvxMJW69nt2js3SuGEWojGnOpCVPRe")); + ASSERT_TRUE(Address::isValid("Ef_drj6m7jcME0fWTA-OwFC-6F0Le2SuOUQ6ibRc3Vz8HL8H")); + + // user-friendly, b64 + ASSERT_TRUE(Address::isValid("EQAN6Dr3vziti1Kp9D3aEFqJX4bBVfCaV57Z+9jwKTBXICv8")); + ASSERT_TRUE(Address::isValid("EQCmGW+z+UL00FmnhWaMvJq/i86YY5GlJP3uJW19KC5Tzq4C")); +} + +TEST(TheOpenNetworkAddress, Invalid) { + ASSERT_FALSE(Address::isValid("random string")); + + // invalid size + ASSERT_FALSE(Address::isValid("EQIcIZpPoMnWXd8FbC1KaLtcyIgVUlwsbFK_3P6f5uf_YyzoE")); + ASSERT_FALSE(Address::isValid("EQIcIZpPoMnWXd8FbC1KaLtcyIgVUlwsbFK_3P6f5uf_YyE")); + + // invalid size after decode + ASSERT_FALSE(Address::isValid("EQIcIZpPoMnWXd8FbC1KaLtcyIgVUlwsbFK_3P6f5uf_Yyw=")); + + // invalid workchain + ASSERT_FALSE(Address::isValid("1:0ccd5119f27f7fe4614476c34f7e5e93c7ae098e577cf2012f8b8043165cb809")); + ASSERT_FALSE(Address::isValid("EQEMzVEZ8n9_5GFEdsNPfl6Tx64Jjld88gEvi4BDFly4CSyl")); + ASSERT_FALSE(Address::isValid("-2:e0e98cfcf743292298ad9e379a3c2e6401797b9cbfc0fe98b4e14fd0ce07ecdf")); + ASSERT_FALSE(Address::isValid("Ef7g6Yz890MpIpitnjeaPC5kAXl7nL_A_pi04U_Qzgfs3-Cj")); + + // invalid tag + ASSERT_FALSE(Address::isValid("MwCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorsn8")); // 0x33 + ASSERT_FALSE(Address::isValid("swCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsornJ2")); // 0x80 + 0x33 + + // invalid crc + ASSERT_FALSE(Address::isValid("EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsormVH")); // crc[a, b] = crc[b, a] + ASSERT_FALSE(Address::isValid("EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorpcF")); // crc=0x9705 +} + +TEST(TheOpenNetworkAddress, FromString) { + auto raw_address = Address("0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae"); + ASSERT_EQ(raw_address.string(), "0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae"); + ASSERT_FALSE(raw_address.isUserFriendly); + + auto raw_address_uppercase = Address("0:8A8627861A5DD96C9DB3CE0807B122DA5ED473934CE7568A5B4B1C361CBB28AE"); + ASSERT_EQ(raw_address_uppercase.string(), "0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae"); + ASSERT_FALSE(raw_address_uppercase.isUserFriendly); + + // 0x11 + auto bounceable_address = Address("EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); + ASSERT_EQ(bounceable_address.string(), "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); + ASSERT_TRUE(bounceable_address.isUserFriendly); + ASSERT_TRUE(bounceable_address.isBounceable); + ASSERT_FALSE(bounceable_address.isTestOnly); + + // 0x51 + auto non_bounceable_address = Address("UQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorhqg"); + ASSERT_EQ(non_bounceable_address.string(), "UQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorhqg"); + ASSERT_TRUE(non_bounceable_address.isUserFriendly); + ASSERT_FALSE(non_bounceable_address.isBounceable); + ASSERT_FALSE(non_bounceable_address.isTestOnly); + + // 0x11 | 0x80 + auto test_bounceable_address = Address("kQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorvzv"); + ASSERT_EQ(test_bounceable_address.string(), "kQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorvzv"); + ASSERT_TRUE(test_bounceable_address.isUserFriendly); + ASSERT_TRUE(test_bounceable_address.isBounceable); + ASSERT_TRUE(test_bounceable_address.isTestOnly); + + // 0x51 | 0x80 + auto test_non_bounceable_address = Address("0QCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorqEq"); + ASSERT_EQ(test_non_bounceable_address.string(), "0QCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorqEq"); + ASSERT_TRUE(test_non_bounceable_address.isUserFriendly); + ASSERT_FALSE(test_non_bounceable_address.isBounceable); + ASSERT_TRUE(test_non_bounceable_address.isTestOnly); +} + +TEST(TheOpenNetworkAddress, FromPrivateKeyV4R2) { + const auto privateKey = PrivateKey(parse_hex("ff3ceb81a22c726e9d61d3f336fc783de5d60020972ca3abc27b99e3cf573a88")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + + WalletV4R2 wallet(publicKey, WorkchainType::Basechain); + const auto address = wallet.getAddress(); + + ASSERT_EQ(address.string(), "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); +} + +TEST(TheOpenNetworkAddress, FromPublicKeyV4R2) { + const auto publicKey = PublicKey(parse_hex("c2036a1ca901059e1d1ab38cd7a7a4709b5e8f9d85b387f0514d7adae70b6afe"), TWPublicKeyTypeED25519); + + WalletV4R2 wallet(publicKey, WorkchainType::Basechain); + const auto address = wallet.getAddress(); + + ASSERT_EQ(address.string(), "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); +} + +} // namespace TW::TheOpenNetwork::tests diff --git a/tests/chains/TheOpenNetwork/SignerTests.cpp b/tests/chains/TheOpenNetwork/SignerTests.cpp new file mode 100644 index 00000000000..2a108204b01 --- /dev/null +++ b/tests/chains/TheOpenNetwork/SignerTests.cpp @@ -0,0 +1,166 @@ +// Copyright © 2017-2022 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 "HexCoding.h" + +#include "TheOpenNetwork/Signer.h" +#include "Everscale/CommonTON/Cell.h" + +#include + +namespace TW::TheOpenNetwork::tests { + +TEST(TheOpenNetworkSigner, TransferAndDeploy) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_wallet_version(Proto::WALLET_V4_R2); + transfer.set_dest("EQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRR3n0"); + transfer.set_amount(10); + transfer.set_mode(Proto::SendMode::PAY_FEES_SEPARATELY | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS); + transfer.set_expire_at(1671135440); + + const auto privateKey = parse_hex("63474e5fe9511f1526a50567ce142befc343e71a49b865ac3908f58667319cb8"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "b3d9462c13a8c67e19b62002447839c386de51415ace3ff6473b1e6294299819"); + + // tx: https://tonscan.org/tx/6ZzWOFKZt_m3kZjbwfbATwLaVwmUOdDp0xjhuY7PO3k= + ASSERT_EQ(output.encoded(), "te6ccgICABoAAQAAA8sAAAJFiADN98eLgHfrkE8l8gmT8X5REpTVR6QnqDhArTbKlVvbZh4ABAABAZznxvGBhoRXhPogxNY8QmHlihJWxg5t6KptqcAIZlVks1r+Z+r1avCWNCeqeLC/oaiVN4mDx/E1+Zhi33G25rcIKamjF/////8AAAAAAAMAAgFiYgBsLf6vJOEq42xW0AoyWX0K+uBMUcXFDLFqmkDg6k1Io4hQAAAAAAAAAAAAAAAAAQADAAACATQABgAFAFEAAAAAKamjF/Qsd/kxvqIOxdAVBzEna7suKGCUdmEkWyMZ74Ez7o1BQAEU/wD0pBP0vPLICwAHAgEgAA0ACAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/wAMAAsACgAJAAr0AMntVABsgQEI1xj6ANM/MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAHCBAQjXGPoA0z/IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgIBSAAXAA4CASAAEAAPAFm9JCtvaiaECAoGuQ+gIYRw1AgIR6STfSmRDOaQPp/5g3gSgBt4EBSJhxWfMYQCASAAEgARABG4yX7UTQ1wsfgCAVgAFgATAgEgABUAFAAZrx32omhAEGuQ64WPwAAZrc52omhAIGuQ64X/wAA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQAZABgAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+gIZ9ADLaRfLH1Jgyz8gyYBA+wAG"); +} + +TEST(TheOpenNetworkSigner, TransferOrdinary) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_wallet_version(Proto::WALLET_V4_R2); + transfer.set_dest("EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + transfer.set_amount(10); + transfer.set_sequence_number(6); + transfer.set_mode(Proto::SendMode::PAY_FEES_SEPARATELY | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS); + transfer.set_expire_at(1671132440); + + const auto privateKey = parse_hex("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "3908cf8b570c1d3d261c62620c9f368db11f6e821a07614cff64de2e7319f81b"); + + // tx: https://tonscan.org/tx/3Z4tHpXNLyprecgu5aTQHWtY7dpHXEoo11MAX61Xyg0= + ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAALAAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcEUPkil2aZ4s8KKparSep/OKHMC8vuXafFbW2HGp/9AcTRv0J5T4dwyW1G0JpHw+g5Ov6QI3Xo0O9RFr3KidICimpoxdjm3UYAAAABgADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwAA"); +} + +TEST(TheOpenNetworkSigner, TransferAllBalance) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_wallet_version(Proto::WALLET_V4_R2); + transfer.set_dest("EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + transfer.set_amount(0); + transfer.set_sequence_number(7); + transfer.set_mode(Proto::SendMode::ATTACH_ALL_CONTRACT_BALANCE | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS); + transfer.set_expire_at(1681102222); + + const auto privateKey = parse_hex("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "d5c5980c9083f697a7f114426effbbafac6d5c88554297d290eb65c8def3008e"); + + // tx: https://tonscan.org/tx/cVcXgI9DWNWlN2iyTsteaWJckTswVqWZnRVvX5krXeA= + ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAAK8AAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGc58rMUQc/u78bg+Wtt8ETkyM0udf7S+F7wWk7lnPib2KChnBx9dZ7a/zLzhfLq+W9LjLZZfx995J17+0sbkvGCympoxdkM5WOAAAABwCCAAIBYGIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmAAAAAAAAAAAAAAAAAAQADAAA="); +} + +TEST(TheOpenNetworkSigner, TransferAllBalanceNonBounceable) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_wallet_version(Proto::WALLET_V4_R2); + transfer.set_dest("UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV"); + transfer.set_amount(0); + transfer.set_sequence_number(8); + transfer.set_mode(Proto::SendMode::ATTACH_ALL_CONTRACT_BALANCE | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS); + transfer.set_expire_at(1681102222); + + const auto privateKey = parse_hex("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "e9c816780fa8e578bae309c2e098db8eb16aa25545b3ad2b61bb711ec9562795"); + + // tx: https://tonscan.org/tx/0sJkPKu6u6uObVRuSWGd_bVGiyy5lJuzEKDqSXifQEA= + ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAAK8AAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcRQQvxdU1u4QoE2Pas0AsZQMc9lea3+wtSvaC6QfLUlyJ9oISMCFnaErpyFHelDhPu4iuZqhkoLwjkR1VYhFSCimpoxdkM5WOAAAACACCAAIBYEIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmAAAAAAAAAAAAAAAAAAQADAAA="); +} + +TEST(TheOpenNetworkSigner, TransferWithASCIIComment) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_wallet_version(Proto::WALLET_V4_R2); + transfer.set_dest("EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + transfer.set_amount(10); + transfer.set_sequence_number(10); + transfer.set_mode(Proto::SendMode::PAY_FEES_SEPARATELY | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS); + transfer.set_expire_at(1681102222); + transfer.set_comment("test comment"); + + const auto privateKey = parse_hex("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "a8c6943d5587f590c43fcdb0e894046f1965c615e19bcaf0c8407e9ccb74518d"); + + // tx: https://tonscan.org/tx/9wjD-VrgEDpa0D9u1g03KSD7kvTNsxRocR7LEdQtCNQ= + ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAAMAAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcY4XlvKqu7spxyjL6vyBSKjbskDgqkHhqBsdTe900RGrzExtpvwc04j94v8HOczEWSMCXjTXk0z+CVUXSL54qCimpoxdkM5WOAAAACgADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwAgAAAAAHRlc3QgY29tbWVudA=="); +} + +TEST(TheOpenNetworkSigner, TransferWithUTF8Comment) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_wallet_version(Proto::WALLET_V4_R2); + transfer.set_dest("EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + transfer.set_amount(10); + transfer.set_sequence_number(11); + transfer.set_mode(Proto::SendMode::PAY_FEES_SEPARATELY | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS); + transfer.set_expire_at(1681102222); + transfer.set_comment("тестовый комментарий"); + + const auto privateKey = parse_hex("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "1091dfae81583d3972825633592c24eab0d3d74c91f60fda9d4afe7535103633"); + + // tx: https://tonscan.org/tx/VOTt8HW6eRuWHmuM_P3aC-Dy4TMu4cCRePoTAiDfcoQ= + ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAANsAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGchoDa7EdGQuPuehHy3+0X9WNVEvYxdBtaEWn15oYUX8PEKyzztYy94Xq0T2XdhVvj2H7PTSQ+D/Ny1IBRCxk0BimpoxdkM5WOAAAACwADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwBWAAAAANGC0LXRgdGC0L7QstGL0Lkg0LrQvtC80LzQtdC90YLQsNGA0LjQuQ=="); +} + +TEST(TheOpenNetworkSigner, InvalidWalletVersion) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_wallet_version(Proto::WALLET_V3_R2); + transfer.set_dest("EQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRR3n0"); + transfer.set_amount(10); + transfer.set_mode(Proto::SendMode::PAY_FEES_SEPARATELY | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS); + transfer.set_expire_at(1671135440); + + const auto privateKey = parse_hex("63474e5fe9511f1526a50567ce142befc343e71a49b865ac3908f58667319cb8"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + ASSERT_EQ(output.error(), 22); +} + +} // namespace TW::TheOpenNetwork::tests diff --git a/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp b/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..e84bcc6061c --- /dev/null +++ b/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2022 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 "Data.h" + +#include +#include + +#include "TestUtilities.h" +#include + +using namespace TW; + +namespace TW::TheOpenNetwork::tests { + +TEST(TWTheOpenNetwork, Address) { + const auto mnemonic = STRING("stuff diamond cycle federal scan spread pigeon people engage teach snack grain"); + const auto passphrase = STRING(""); + + const auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(mnemonic.get(), passphrase.get())); + + const auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), TWCoinTypeTON, WRAPS(TWCoinTypeDerivationPath(TWCoinTypeTON)).get())); + const auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519(privateKey.get())); + const auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeTON)); + const auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); + + assertStringsEqual(addressStr, "EQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRR3n0"); +} + +} // namespace TW::TheOpenNetwork::tests diff --git a/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp b/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp new file mode 100644 index 00000000000..3becb96799a --- /dev/null +++ b/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 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 "HexCoding.h" + +#include "proto/TheOpenNetwork.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::TheOpenNetwork::tests { + +TEST(TWAnySignerTheOpenNetwork, SingMessageToTransferAndDeployWallet) { + Proto::SigningInput input; + + auto& transfer = *input.mutable_transfer(); + transfer.set_wallet_version(Proto::WALLET_V4_R2); + transfer.set_dest("EQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRR3n0"); + transfer.set_amount(10); + transfer.set_mode(Proto::SendMode::PAY_FEES_SEPARATELY | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS); + transfer.set_expire_at(1671135440); + + const auto privateKey = parse_hex("63474e5fe9511f1526a50567ce142befc343e71a49b865ac3908f58667319cb8"); + input.set_private_key(privateKey.data(), privateKey.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeTON); + + ASSERT_EQ(output.encoded(), "te6ccgICABoAAQAAA8sAAAJFiADN98eLgHfrkE8l8gmT8X5REpTVR6QnqDhArTbKlVvbZh4ABAABAZznxvGBhoRXhPogxNY8QmHlihJWxg5t6KptqcAIZlVks1r+Z+r1avCWNCeqeLC/oaiVN4mDx/E1+Zhi33G25rcIKamjF/////8AAAAAAAMAAgFiYgBsLf6vJOEq42xW0AoyWX0K+uBMUcXFDLFqmkDg6k1Io4hQAAAAAAAAAAAAAAAAAQADAAACATQABgAFAFEAAAAAKamjF/Qsd/kxvqIOxdAVBzEna7suKGCUdmEkWyMZ74Ez7o1BQAEU/wD0pBP0vPLICwAHAgEgAA0ACAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/wAMAAsACgAJAAr0AMntVABsgQEI1xj6ANM/MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAHCBAQjXGPoA0z/IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgIBSAAXAA4CASAAEAAPAFm9JCtvaiaECAoGuQ+gIYRw1AgIR6STfSmRDOaQPp/5g3gSgBt4EBSJhxWfMYQCASAAEgARABG4yX7UTQ1wsfgCAVgAFgATAgEgABUAFAAZrx32omhAEGuQ64WPwAAZrc52omhAIGuQ64X/wAA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQAZABgAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+gIZ9ADLaRfLH1Jgyz8gyYBA+wAG"); +} + +} // namespace TW::TheOpenNetwork::tests + diff --git a/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp b/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..83e8e08d400 --- /dev/null +++ b/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2022 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. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include + +#include "TestUtilities.h" +#include + + +TEST(TWTONCoinType, TWCoinType) { + const auto coin = TWCoinTypeTON; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("fJXfn0EVhV09HFuEgUHu4Cchb24nUQtIMwSzmzk2tLs=")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "ton"); + assertStringsEqual(name, "TON"); + assertStringsEqual(symbol, "TON"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 9); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainTheOpenNetwork); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(txUrl, "https://tonscan.org/tx/fJXfn0EVhV09HFuEgUHu4Cchb24nUQtIMwSzmzk2tLs="); + assertStringsEqual(accUrl, "https://tonscan.org/address/EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"); +} diff --git a/tests/common/Base64Tests.cpp b/tests/common/Base64Tests.cpp index 64787ffaaf8..1d61102f7ff 100644 --- a/tests/common/Base64Tests.cpp +++ b/tests/common/Base64Tests.cpp @@ -61,4 +61,15 @@ TEST(Base64, UrlFormat) { EXPECT_EQ(const1, hex(decoded)); } +TEST(Base64, isBase64) { + EXPECT_TRUE(isBase64orBase64Url("Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb")); + EXPECT_TRUE(isBase64orBase64Url("Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSk=")); + EXPECT_TRUE(isBase64orBase64Url("Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODS==")); + EXPECT_TRUE(isBase64orBase64Url("EQA_qoVWKJl17JkayZlN-2E6vsTqAA1QlOY3kID1lOVZszC4")); + EXPECT_FALSE(isBase64orBase64Url("Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcOD===")); + EXPECT_FALSE(isBase64orBase64Url("Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb=")); + EXPECT_FALSE(isBase64orBase64Url("Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSk")); + EXPECT_FALSE(isBase64orBase64Url("MwCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsors=#")); +} + } // namespace TW::Base64::tests diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 167041878b2..71982cf50cd 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -153,6 +153,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeEverscale: EXPECT_EQ(address, "0:ef64d51f95ef17973b737277cfecbd2a8d551141be2f58f5fb362575fc3eb5b0"); break; + case TWCoinTypeTON: + EXPECT_EQ(address, "EQAoYT8nMLfeNh6h0uIoK_wLm9JkvxiGxJDr6GRXJGu2ZhpY"); + break; case TWCoinTypeFIO: EXPECT_EQ(address, "FIO5TrYnZP1RkDSUMzBY4GanCy6AP68kCMdkAb5EACkAwkdgRLShz"); break; diff --git a/tests/common/CoinAddressValidationTests.cpp b/tests/common/CoinAddressValidationTests.cpp index a1816d6e911..5f899c5c9f5 100644 --- a/tests/common/CoinAddressValidationTests.cpp +++ b/tests/common/CoinAddressValidationTests.cpp @@ -409,4 +409,11 @@ TEST(Coin, ValidateAddressEverscale) { ASSERT_EQ(normalizeAddress(TWCoinTypeEverscale, "0:83A0352908060FA87839195D8A763A8D9AB28F8FA41468832B398A719CC6469A"), "0:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a"); } +TEST(Coin, ValidateAddressTheOpenNetwork) { + EXPECT_TRUE(validateAddress(TWCoinTypeTON, "0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae")); + EXPECT_FALSE(validateAddress(TWCoinTypeTON, "8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae")); + + ASSERT_EQ(normalizeAddress(TWCoinTypeTON, "0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae"), "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); +} + } // namespace TW diff --git a/tests/common/HexCodingTests.cpp b/tests/common/HexCodingTests.cpp index 57a40717fed..91ebe83150d 100644 --- a/tests/common/HexCodingTests.cpp +++ b/tests/common/HexCodingTests.cpp @@ -28,4 +28,11 @@ TEST(HexCoding, OddLength) { ASSERT_EQ(number, 11000000000); } +TEST(HexCoding, isHexEncoded) { + ASSERT_TRUE(is_hex_encoded("66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3")); + ASSERT_TRUE(is_hex_encoded("0x66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3")); + ASSERT_FALSE(is_hex_encoded("1x66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3")); + ASSERT_FALSE(is_hex_encoded("0xyahoo")); +} + } diff --git a/wasm/tests/Blockchain/TheOpenNetwork.test.ts b/wasm/tests/Blockchain/TheOpenNetwork.test.ts new file mode 100644 index 00000000000..81ce7f523ff --- /dev/null +++ b/wasm/tests/Blockchain/TheOpenNetwork.test.ts @@ -0,0 +1,100 @@ +// Copyright © 2017-2022 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. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; +import { TW } from "../../dist"; +import Long = require("long"); + +describe("TheOpenNetwork", () => { + it("test address from private key TheOpenNetwork", () => { + const { PrivateKey, HexCoding, AnyAddress, CoinType, Curve } = globalThis.core; + let data = HexCoding.decode("63474e5fe9511f1526a50567ce142befc343e71a49b865ac3908f58667319cb8"); + let privateKey = PrivateKey.createWithData(data); + + assert.isTrue(PrivateKey.isValid(data, Curve.ed25519)); + + let publicKey = privateKey.getPublicKeyEd25519(); + let address = AnyAddress.createWithPublicKey(publicKey, CoinType.ton) + + assert.equal(publicKey.description(), "f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41"); + assert.equal(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + }); + + it("test address from public key TheOpenNetwork", () => { + const { PublicKey, PublicKeyType, HexCoding, AnyAddress, CoinType } = globalThis.core; + let publicKey = PublicKey.createWithData(HexCoding.decode("f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41"), PublicKeyType.ed25519); + let address = AnyAddress.createWithPublicKey(publicKey, CoinType.ton); + assert.equal(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + }); + + it("test address from raw string TheOpenNetwork", () => { + const { AnyAddress, CoinType } = globalThis.core; + let addressString = "0:66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3"; + let address = AnyAddress.createWithString(addressString, CoinType.ton); + assert.equal(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + }); + + it("test address invalid hex TheOpenNetwork", () => { + const { AnyAddress, CoinType } = globalThis.core; + let addressString = "0:yahoo3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3"; + let valid = AnyAddress.isValid(addressString, CoinType.ton); + assert.isFalse(valid); + }); + + it("test address invalid workchain id TheOpenNetwork", () => { + const { AnyAddress, CoinType } = globalThis.core; + let addressString = "a:66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3"; + let valid = AnyAddress.isValid(addressString, CoinType.ton); + assert.isFalse(valid); + }); + + it("test address from user friendly string TheOpenNetwork", () => { + const { AnyAddress, CoinType } = globalThis.core; + let addressString = "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"; + let address = AnyAddress.createWithString(addressString, CoinType.ton); + assert.equal(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + }); + + it("test address from user friendly invalid base64 decoding TheOpenNetwork", () => { + const { AnyAddress, CoinType } = globalThis.core; + let addressString = "MwCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsors=#"; + let valid = AnyAddress.isValid(addressString, CoinType.ton); + assert.isFalse(valid); + }); + + it("test sign TheOpenNetwork", () => { + const { PrivateKey, HexCoding, CoinType, AnySigner } = globalThis.core; + + let privateKeyData = HexCoding.decode("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0"); + + let transfer = TW.TheOpenNetwork.Proto.Transfer.create({ + walletVersion: TW.TheOpenNetwork.Proto.WalletVersion.WALLET_V4_R2, + dest: "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q", + amount: new Long(10), + sequenceNumber: 6, + mode: (TW.TheOpenNetwork.Proto.SendMode.PAY_FEES_SEPARATELY | TW.TheOpenNetwork.Proto.SendMode.IGNORE_ACTION_PHASE_ERRORS), + expireAt: 1671132440 + }); + + let input = TW.TheOpenNetwork.Proto.SigningInput.create({ + transfer: transfer, + privateKey: PrivateKey.createWithData(privateKeyData).data(), + }); + + const encoded = TW.TheOpenNetwork.Proto.SigningInput.encode(input).finish(); + let outputData = AnySigner.sign(encoded, CoinType.ton); + let output = TW.TheOpenNetwork.Proto.SigningOutput.decode(outputData); + + // tx: https://tonscan.org/tx/3Z4tHpXNLyprecgu5aTQHWtY7dpHXEoo11MAX61Xyg0= + let expectedString = "te6ccgICAAQAAQAAALAAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcEUPkil2aZ4s8KKparSep/OKHMC8vuXafFbW2HGp/9AcTRv0J5T4dwyW1G0JpHw+g5Ov6QI3Xo0O9RFr3KidICimpoxdjm3UYAAAABgADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwAA"; + + assert.equal(output.encoded, expectedString) + }); +}); + + From 14a14b2f0c9f059fd4adb1c258cda30f56b6ba70 Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Fri, 13 Jan 2023 14:40:39 +0000 Subject: [PATCH 076/426] [WAX]: Add WAX chain (#2783) --- .../blockchains/CoinAddressDerivationTests.kt | 1 + docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 26 +++++++++ swift/Tests/CoinAddressDerivationTests.swift | 2 +- tests/chains/WAX/TWAnySignerTests.cpp | 56 +++++++++++++++++++ tests/chains/WAX/TWCoinTypeTests.cpp | 30 ++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 8 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/chains/WAX/TWAnySignerTests.cpp create mode 100644 tests/chains/WAX/TWCoinTypeTests.cpp 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 d1eec662f19..dd3b7491a5c 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,6 +75,7 @@ class CoinAddressDerivationTests { QTUM -> assertEquals("QhceuaTdeCZtcxmVc6yyEDEJ7Riu5gWFoF", address) NULS -> assertEquals("NULSd6HgU8MoRnNjBgvJpa9tqvGxYdv5ne4en", address) EOS -> assertEquals("EOS6hs8sRvGSzuQtq223zwJipMzqTJpXUVjyvHPvPwBSZWWrJTJkg", address) + WAX -> assertEquals("EOS6hs8sRvGSzuQtq223zwJipMzqTJpXUVjyvHPvPwBSZWWrJTJkg", address) IOTEX -> assertEquals("io1qw9cccecw09q7p5kzyqtuhfhvah2mhfrc84jfk", address) ZILLIQA -> assertEquals("zil1mk6pqphhkmaguhalq6n3cq0h38ltcehg0rfmv6", address) ZELCASH -> assertEquals("t1UKbRPzL4WN8Rs8aZ8RNiWoD2ftCMHKGUf", address) diff --git a/docs/registry.md b/docs/registry.md index 51cb097e664..0f0784f6e60 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -73,6 +73,7 @@ This list is generated from [./registry.json](../registry.json) | 3030 | Hedera | HBAR | | | | 6060 | GoChain | GO | | | | 8964 | NULS | NULS | | | +| 14001 | WAX | WAXP | | | | 18000 | Meter | MTR | | | | 19167 | Flux | FLUX | | | | 52752 | Celo | CELO | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 661ccebf68d..39b3c9bdeb0 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -43,6 +43,7 @@ enum TWCoinType { TWCoinTypeDigiByte = 20, TWCoinTypeDogecoin = 3, TWCoinTypeEOS = 194, + TWCoinTypeWAX = 14001, TWCoinTypeEthereum = 60, TWCoinTypeEthereumClassic = 61, TWCoinTypeFIO = 235, diff --git a/registry.json b/registry.json index 81df647a241..2bfa2e3ad3c 100644 --- a/registry.json +++ b/registry.json @@ -759,6 +759,32 @@ "documentation": "https://developers.eos.io/eosio-nodeos/reference" } }, + { + "id": "wax", + "name": "WAX", + "coinId": 14001, + "symbol": "WAXP", + "decimals": 4, + "blockchain": "EOS", + "derivation": [ + { + "path": "m/44'/194'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "explorer": { + "url": "https://wax.bloks.io", + "txPath": "/transaction/", + "accountPath": "/account/" + }, + "info": { + "url": "http://wax.io", + "source": "https://github.com/worldwide-asset-exchange/wax-blockchain", + "rpc": "https://wax.blacklusion.io", + "documentation": "https://https://developer.wax.io" + } + }, { "id": "tron", "name": "Tron", diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 3d46f67044c..1863b13d3b5 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -67,7 +67,7 @@ class CoinAddressDerivationTests: XCTestCase { case .elrond: let expectedResult = "erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8" assertCoinDerivation(coin, expectedResult, derivedAddress, address) - case .eos: + case .eos, .wax: let expectedResult = "EOS6hs8sRvGSzuQtq223zwJipMzqTJpXUVjyvHPvPwBSZWWrJTJkg" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ethereum, diff --git a/tests/chains/WAX/TWAnySignerTests.cpp b/tests/chains/WAX/TWAnySignerTests.cpp new file mode 100644 index 00000000000..4074293d5e3 --- /dev/null +++ b/tests/chains/WAX/TWAnySignerTests.cpp @@ -0,0 +1,56 @@ +// Copyright © 2017-2022 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 "TestUtilities.h" +#include +#include "HexCoding.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include "EOS/Address.h" +#include "Base58.h" +#include "proto/EOS.pb.h" + +#include + +namespace TW::EOS::tests { + +TEST(TWAnySignerWAX, Sign) { + Proto::SigningInput input; + const auto chainId = parse_hex("1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4"); + const auto refBlock = parse_hex("0cffaeda15039f3468398c5b4295d220fcc217f7cf96030c3729773097c6bd76"); + const auto key = parse_hex("d30d185a296b9591d648cb92fe0aa8f8a42de30ed9d2a21da9e7f69c67e8e355"); + + const auto pubKey = PublicKey(PrivateKey(key).getPublicKey(TWPublicKeyTypeSECP256k1)); + const auto address = Address(pubKey); + EXPECT_EQ(address.string(), "EOS7rC6zYUjuxWkiokZTrwwHqwFvZ15Qdrn5WNxMKVXtHiDDmBWog"); + + auto& asset = *input.mutable_asset(); + asset.set_amount(100000000); + asset.set_decimals(4); + asset.set_symbol("WAX"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_reference_block_id(refBlock.data(), refBlock.size()); + input.set_reference_block_time(1670507804); + input.set_currency("eosio.token"); + input.set_sender("k52o1qdeh.gm"); + input.set_recipient("c2lrpvzxb.gm"); + input.set_memo("sent from wallet-core"); + input.set_private_key(key.data(), key.size()); + input.set_private_key_type(Proto::KeyType::MODERNK1); + + // https://wax.bloks.io/transaction/4548f7b28ee608663caea61234049ac0018415e02dd0abcea1c215c8da00d10a + { + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEOS); + + EXPECT_EQ(output.error(), Common::Proto::OK); + auto expected = R"({"compression":"none","packed_context_free_data":"","packed_trx":"3aed9163daae68398c5b000000000100a6823403ea3055000000572d3ccdcd012019682ad940458100000000a8ed3232362019682ad9404581201938fdef7aa34000e1f5050000000004574158000000001573656e742066726f6d2077616c6c65742d636f726500","signatures":["SIG_K1_KAroa9t89dpujjfBgBMgDcZrVhML5yP7iFk5sGNnNqbT4SxTCLqjQwwLZDi1ryx4W7Hy9DE9p1MqUSFVKeY8NtKyiySFjE"]})"; + EXPECT_EQ(output.json_encoded(), expected); + } +} + +} // namespace TW::EOS::tests diff --git a/tests/chains/WAX/TWCoinTypeTests.cpp b/tests/chains/WAX/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..95e2880dc77 --- /dev/null +++ b/tests/chains/WAX/TWCoinTypeTests.cpp @@ -0,0 +1,30 @@ +// 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 "TestUtilities.h" +#include +#include + + +TEST(TWWAXCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeWAX)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("4548f7b28ee608663caea61234049ac0018415e02dd0abcea1c215c8da00d10a")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeWAX, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("k52o1qdeh.gm")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeWAX, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeWAX)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeWAX)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeWAX), 4); + ASSERT_EQ(TWBlockchainEOS, TWCoinTypeBlockchain(TWCoinTypeWAX)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeWAX)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeWAX)); + assertStringsEqual(symbol, "WAXP"); + assertStringsEqual(txUrl, "https://wax.bloks.io/transaction/4548f7b28ee608663caea61234049ac0018415e02dd0abcea1c215c8da00d10a"); + assertStringsEqual(accUrl, "https://wax.bloks.io/account/k52o1qdeh.gm"); + assertStringsEqual(id, "wax"); + assertStringsEqual(name, "WAX"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 71982cf50cd..34b27fce10e 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -145,6 +145,7 @@ TEST(Coin, DeriveAddress) { EXPECT_EQ(address, "ecash:qz7eyzytkl5z6cg6nw20hd62pyyp22mcfuywezks2y"); break; case TWCoinTypeEOS: + case TWCoinTypeWAX: EXPECT_EQ(address, "EOS5TrYnZP1RkDSUMzBY4GanCy6AP68kCMdkAb5EACkAwkdgRLShz"); break; case TWCoinTypeElrond: From fd2fac821c6552c6147b87ce7341bca2d70e1cb4 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 17 Jan 2023 16:08:29 +0100 Subject: [PATCH 077/426] [Misc]: add SECURITY.MD (#2872) --- SECURITY.MD | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 SECURITY.MD diff --git a/SECURITY.MD b/SECURITY.MD new file mode 100644 index 00000000000..720baa249ff --- /dev/null +++ b/SECURITY.MD @@ -0,0 +1,29 @@ +# Security Policy + +The security of our users' assets is of the utmost importance to us. We take a number of steps to ensure that our crypto wallet is as secure as possible. + +## Reporting a Security Vulnerability + +If you believe you have found a security vulnerability in our wallet, please contact us immediately at [Bug Bounty Binance](https://bugcrowd.com/binance). We will investigate all reports and do our best to quickly fix any vulnerabilities. + +## Responsible Disclosure + +IMPORTANT: Do not file public issues on GitHub for security vulnerabilities. Do not publicly disclose the vulnerability until we have had a chance to patch it. This gives us time to fix the problem and protect our users’ assets. + +## Encryption + +All private keys are encrypted and stored on the user's device. The encryption uses industry-standard algorithms and is designed to protect against brute-force attacks. + +## Regular Audits + +We regularly conduct security audits of our code to ensure that it is free of vulnerabilities. We also stay up-to-date with the latest security best practices and technologies. + +## Bug Bounty Program + +As a part of Binance security program, TrustWallet also participate in their bug bounty program. For more information on eligible scope, rewards, and how to submit a report, please visit [https://bugcrowd.com/binance](https://bugcrowd.com/binance) + +## Disclaimer + +As with any software, there are always potential security risks. We do our best to minimize these risks and keep our users' assets safe, but we cannot guarantee that our wallet will be completely immune to all security threats. + + From de91d9068865129ccaca1562c7f6f51f7fd77da5 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 18 Jan 2023 09:05:26 +0100 Subject: [PATCH 078/426] [ThorSwap]: fee optimization for EVM chains (#2868) --- .../thorchain/TestTHORSwapSigning.kt | 4 +- src/THORChain/Swap.cpp | 35 +- .../Blockchains/THORChainSwapTests.swift | 4 +- tests/chains/THORChain/SwapTests.cpp | 526 ++++++++---------- tests/chains/THORChain/TWSwapTests.cpp | 4 +- 5 files changed, 265 insertions(+), 308 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt index fcc0dd64986..68f0f8acc55 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt @@ -48,7 +48,7 @@ class TestTHORChainSwap { // invoke swap val outputData = buildSwap(inputSerialized) - assertEquals(outputData.count(), 375) + assertEquals(outputData.count(), 192) // parse result in proto val outputProto = THORChainSwap.SwapOutput.newBuilder().mergeFrom(outputData) @@ -70,6 +70,6 @@ class TestTHORChainSwap { // sign and encode resulting input val output = AnySigner.sign(txInputFull, ETHEREUM, SigningOutput.parser()) - assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0xf90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000000000026a0ee68bd41da9a9b1ad87fd547e83e4b8022460de024839f4f5f528abc6aecf2aea0402205812d62a075138743f6048ba2a1c073f4a3a14224009a34ee74d3dccef1") + assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0x02f8d90103808083013880941091c4de6a3cf09cda00abdaed42c7c3b69c83ec87b1a2bc2ec50000b86e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130c001a05c16871b66fd0fa8f658d6f171310bab332d09e0533d6c97329a59ddc93a9a11a05ed2be94e6dbb640e58920c8be4fa597cd5f0a918123245acb899042dd43777f") } } diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index 8cf5c44be2d..cb42e556b81 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -7,6 +7,7 @@ #include "Swap.h" #include "Coin.h" +#include "HexCoding.h" #include // ATOM @@ -228,13 +229,15 @@ SwapBundled SwapBuilder::buildBinance(Proto::Asset fromAsset, uint64_t amount, c SwapBundled SwapBuilder::buildEth(uint64_t amount, const std::string& memo) { Data out; auto input = Ethereum::Proto::SigningInput(); + // EIP-1559 + input.set_tx_mode(Ethereum::Proto::Enveloped); const auto& toTokenId = mFromAsset.token_id(); // some sanity check / address conversion Data vaultAddressBin = ethAddressStringToData(mVaultAddress); if (!Ethereum::Address::isValid(mVaultAddress) || vaultAddressBin.size() != Ethereum::Address::size) { return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_vault_address), .error = "Invalid vault address: " + mVaultAddress}; } - if (!Ethereum::Address::isValid(*mRouterAddress)) { + if (!toTokenId.empty() && !Ethereum::Address::isValid(*mRouterAddress)) { return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_router_address), .error = "Invalid router address: " + *mRouterAddress}; } Data toAssetAddressBin = ethAddressStringToData(toTokenId); @@ -252,17 +255,25 @@ SwapBundled SwapBuilder::buildEth(uint64_t amount, const std::string& memo) { // ... end input.set_to_address(*mRouterAddress); - auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); - auto func = Ethereum::ABI::Function("deposit", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(toAssetAddressBin), - std::make_shared(uint256_t(amount)), - std::make_shared(memo)}); - Data payload; - func.encode(payload); - transfer.set_data(payload.data(), payload.size()); - Data amountData = store(toTokenId.empty() ? uint256_t(amount) : uint256_t(0)); - transfer.set_amount(amountData.data(), amountData.size()); + if (!toTokenId.empty()) { + auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); + auto func = Ethereum::ABI::Function("deposit", std::vector>{ + std::make_shared(vaultAddressBin), + std::make_shared(toAssetAddressBin), + std::make_shared(uint256_t(amount)), + std::make_shared(memo)}); + Data payload; + func.encode(payload); + transfer.set_data(payload.data(), payload.size()); + Data amountData = store(uint256_t(0)); + transfer.set_amount(amountData.data(), amountData.size()); + } else { + input.set_to_address(mVaultAddress); + auto& transfer = *input.mutable_transaction()->mutable_transfer(); + Data amountData = store(uint256_t(amount)); + transfer.set_amount(amountData.data(), amountData.size()); + transfer.set_data(memo.data(), memo.size()); + } auto serialized = input.SerializeAsString(); out.insert(out.end(), serialized.begin(), serialized.end()); diff --git a/swift/Tests/Blockchains/THORChainSwapTests.swift b/swift/Tests/Blockchains/THORChainSwapTests.swift index 73500bd9e25..8a7dc4a2ff6 100644 --- a/swift/Tests/Blockchains/THORChainSwapTests.swift +++ b/swift/Tests/Blockchains/THORChainSwapTests.swift @@ -36,7 +36,7 @@ class THORSwapTests: XCTestCase { // invoke swap let outputData = THORChainSwap.buildSwap(input: inputSerialized) - XCTAssertEqual(outputData.count, 375) + XCTAssertEqual(outputData.count, 192) // parse result in proto let outputProto = try THORChainSwapSwapOutput(serializedData: outputData) @@ -55,7 +55,7 @@ class THORSwapTests: XCTestCase { // sign and encode resulting input let output: EthereumSigningOutput = AnySigner.sign(input: txInput, coin: .ethereum) - XCTAssertEqual(output.encoded.hexString, "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000000000026a0ee68bd41da9a9b1ad87fd547e83e4b8022460de024839f4f5f528abc6aecf2aea0402205812d62a075138743f6048ba2a1c073f4a3a14224009a34ee74d3dccef1") + XCTAssertEqual(output.encoded.hexString, "02f8d90103808083013880941091c4de6a3cf09cda00abdaed42c7c3b69c83ec87b1a2bc2ec50000b86e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130c001a05c16871b66fd0fa8f658d6f171310bab332d09e0533d6c97329a59ddc93a9a11a05ed2be94e6dbb640e58920c8be4fa597cd5f0a918123245acb899042dd43777f") } func testSignerBnbBtc() throws { diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 2ecd1094172..f47bc022a34 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -13,8 +13,8 @@ #include "Ethereum/Address.h" #include "THORChain/Swap.h" #include "proto/Binance.pb.h" -#include "proto/Cosmos.pb.h" #include "proto/Bitcoin.pb.h" +#include "proto/Cosmos.pb.h" #include "proto/Ethereum.pb.h" #include "proto/THORChainSwap.pb.h" @@ -48,15 +48,15 @@ TEST(THORChainSwap, SwapBtcEth) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::ETH)); toAsset.set_symbol("ETH"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Btc) - .toAddress(Address1Eth) - .vault(VaultBtc) - .fromAmount("1000000") - .toAmountLimit("140000000000000000") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Eth) + .vault(VaultBtc) + .fromAmount("1000000") + .toAmountLimit("140000000000000000") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); EXPECT_EQ(hex(out), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a473d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313430303030303030303030303030303030"); @@ -92,19 +92,19 @@ TEST(THORChainSwap, SwapBtcEth) { ANY_SIGN(tx, TWCoinTypeBitcoin); EXPECT_EQ(output.error(), 0); EXPECT_EQ(hex(output.encoded()), // printed using prettyPrintTransaction - "01000000" // version - "0001" // marker & flag - "01" // inputs - "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" - "03" // outputs - "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" - "d49ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "49" "6a473d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313430303030303030303030303030303030" - // witness - "02" - "48" "3045022100a67f84cbde5affbb46ffff2b33c1453ff2de70ef990fc974175d9a609e5a87ed0220589c57d958208f866c9477c7d6c9075dea4c58622debb02eab85032b8b6d373001" - "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" - "00000000" // nLockTime + "01000000" // version + "0001" // marker & flag + "01" // inputs + "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" + "03" // outputs + "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" + "d49ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "49" "6a473d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313430303030303030303030303030303030" + // witness + "02" + "48" "3045022100a67f84cbde5affbb46ffff2b33c1453ff2de70ef990fc974175d9a609e5a87ed0220589c57d958208f866c9477c7d6c9075dea4c58622debb02eab85032b8b6d373001" + "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" + "00000000" // nLockTime ); } @@ -119,7 +119,7 @@ TEST(THORChainSwap, SwapDogeBusd) { auto vaultDoge = "DExct9oTfqr7pfnbP2hkCHP1Z2eUDgqXya"; auto fromAddressDoge = "DKftkYCtCyYxQy2TRAuAzQXoyKDdYsEBnw"; auto toAddressBnb = "bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2"; - auto && [out, errorCode, error] = SwapBuilder::builder() + auto&& [out, errorCode, error] = SwapBuilder::builder() .from(fromAsset) .to(toAsset) .fromAddress(fromAddressDoge) @@ -184,7 +184,7 @@ TEST(THORChainSwap, SwapLtcBusd) { auto vaultLTC = "ltc1qmca5runvg3hygarulu34evdulcdfda7z7zquhn"; auto fromAddressLTC = "ltc1qyu9qvkukx99r6yadxlk3t2x78a7dxe73s3r4x3"; auto toAddressBnb = "bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2"; - auto && [out, errorCode, error] = SwapBuilder::builder() + auto&& [out, errorCode, error] = SwapBuilder::builder() .from(fromAsset) .to(toAsset) .fromAddress(fromAddressLTC) @@ -248,7 +248,7 @@ TEST(THORChainSwap, SwapBchBusd) { auto vaultBCH = "qpsfh5xvk7mgf9e6kl4e045nm6awl5hmks9x7h5ad6"; auto fromAddressBCH = "qr50u7hy3xcr3j0w9j5nfx2gevjqgfm42ykc2hqgy4"; auto toAddressBnb = "bnb1s4kallxngpyspzm6nrezkml9rgyw6kxpw4fhr2"; - auto && [out, errorCode, error] = SwapBuilder::builder() + auto&& [out, errorCode, error] = SwapBuilder::builder() .from(fromAsset) .to(toAsset) .fromAddress(fromAddressBCH) @@ -308,15 +308,15 @@ TEST(THORChainSwap, SwapBtcBnb) { toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Btc) - .toAddress(Address1Bnb) - .vault(VaultBtc) - .fromAmount("200000") - .toAmountLimit("140000000") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Bnb) + .vault(VaultBtc) + .fromAmount("200000") + .toAmountLimit("140000000") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); EXPECT_EQ(hex(out), "080110c09a0c1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a3e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030"); @@ -353,19 +353,19 @@ TEST(THORChainSwap, SwapBtcBnb) { ANY_SIGN(tx, TWCoinTypeBitcoin); EXPECT_EQ(output.error(), 0); EXPECT_EQ(hex(output.encoded()), // printed using prettyPrintTransaction - "01000000" // version - "0001" // marker & flag - "01" // inputs - "eb48da786cbd9430bf5ef3d1d3bc7206a4182fd7d5ac3f4e8d05754c3a5cae8e" "00000000" "00" "" "fcffffff" - "03" // outputs - "400d030000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" - "b08d030000000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "40" "6a3e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030" - // witness - "02" - "48" "3045022100e17d8cf207c79edfb7afa16102842b434e1f908bd9858553fd54970f1a8b4334022059583f89c3a126df0da46d92947bcbe7c265a1bb838b696c0e7ea7fc8761c2bf01210" - "21" "e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" - "00000000" // nLockTime + "01000000" // version + "0001" // marker & flag + "01" // inputs + "eb48da786cbd9430bf5ef3d1d3bc7206a4182fd7d5ac3f4e8d05754c3a5cae8e" "00000000" "00" "" "fcffffff" + "03" // outputs + "400d030000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" + "b08d030000000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "40" "6a3e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030" + // witness + "02" + "48" "3045022100e17d8cf207c79edfb7afa16102842b434e1f908bd9858553fd54970f1a8b4334022059583f89c3a126df0da46d92947bcbe7c265a1bb838b696c0e7ea7fc8761c2bf01210" + "21" "e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" + "00000000" // nLockTime ); // similar real transaction: @@ -382,7 +382,7 @@ TEST(THORChainSwap, SwapAtomBnb) { toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); - auto && [out, errorCode, error] = SwapBuilder::builder() + auto&& [out, errorCode, error] = SwapBuilder::builder() .from(fromAsset) .to(toAsset) .fromAddress("cosmos1v4e6vpehwrfez2dqepnw9g6t4fl83xzegd5ac9") @@ -407,7 +407,6 @@ TEST(THORChainSwap, SwapAtomBnb) { fee_amount.set_denom("uatom"); fee_amount.set_amount("500"); - tx.set_account_number(1483163); tx.set_sequence(1); @@ -442,7 +441,7 @@ TEST(THORChainSwap, SwapErc20Rune) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::THOR)); toAsset.set_symbol("RUNE"); - auto && [out, errorCode, error] = SwapBuilder::builder() + auto&& [out, errorCode, error] = SwapBuilder::builder() .from(fromAsset) .to(toAsset) .fromAddress("0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37") @@ -454,7 +453,7 @@ TEST(THORChainSwap, SwapErc20Rune) { .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); - EXPECT_EQ(hex(out), "0a01001201002201002a0100422a307844333742624535373434443733306131643938643844433937633432463043613436614437313436528d02328a020a01001284021fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a34313834313035323000000000000000000000000000000000000000000000000000000000000000"); + EXPECT_EQ(hex(out), "0a010012010018012201002a0100422a307844333742624535373434443733306131643938643844433937633432463043613436614437313436528d02328a020a01001284021fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a34313834313035323000000000000000000000000000000000000000000000000000000000000000"); auto tx = Ethereum::Proto::SigningInput(); ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); @@ -493,7 +492,7 @@ TEST(THORChainSwap, SwapErc20Rune) { // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeEthereum); - EXPECT_EQ(hex(output.encoded()), "f9016b078506fc23ac008301388094d37bbe5744d730a1d98d8dc97c42f0ca46ad714680b901041fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a3431383431303532300000000000000000000000000000000000000000000000000000000000000026a03b9082870fda839820dd36d4da3d8985807c799a8cf8e1971374a461da5899a7a0383d9ceaacf6c90205d4381b403687c17c2bbfaac1c1329ac65c0ce22d940451"); + EXPECT_EQ(hex(output.encoded()), "02f90169010780808301388094d37bbe5744d730a1d98d8dc97c42f0ca46ad714680b901041fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a34313834313035323000000000000000000000000000000000000000000000000000000000000000c001a01ff085d06b39d6efeb6663b065758f463564a555e41070ca8a8398bb1fc3426ba018bd8c6897f86d6ca4af7fe4cfac92e3fcb6224f896df62376ac1df556744ac6"); // https://viewblock.io/thorchain/tx/56D2A63608E6EC09FA1D2934457CC09196683013905F69EDFC72B33EC68681AA // https://etherscan.io/tx/0x56d2a63608e6ec09fa1d2934457cc09196683013905f69edfc72b33ec68681aa // https://viewblock.io/thorchain/tx/BC1464CF3B56B07E40CF57985511814AEC9EAE2F1329CEE059A21529FDDFDB8C @@ -505,57 +504,48 @@ TEST(THORChainSwap, SwapAvaxBnb) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); - auto && [out, errorCode, error] = SwapBuilder::builder() + auto&& [out, errorCode, error] = SwapBuilder::builder() .from(fromAsset) .to(toAsset) - .fromAddress("0x4340fb8bb31357559607142b37C2173418E36785") - .toAddress("bnb1hy9a63tgsvham463vw2tkkw2j5y20v5drhw8p2") - .vault("0x53595320f158d4546677B4795Cc66dfF59D154Db") - .router("0x8f66c4ae756bebc49ec8b81966dd8bba9f127549") - .fromAmount("20000000000000000") + .fromAddress("0xbB7cF2f05a01DB5394234FE1257D907059edFa66") + .toAddress("bnb16gk7gczst59wy8rnxrqnt3yn6f60uw6ec0w6uv") + .vault("0x3bd92906c60e5843ce01b2dc54e6dc3575b5215a") + .fromAmount("150000000000000000") + .toAmountLimit("297039") + .affFeeAddress("t") + .affFeeRate("0") .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); - EXPECT_EQ(hex(out), "0a01001201002201002a0100422a30783866363663346165373536626562633439656338623831393636646438626261396631323735343952f30132f0010a07470de4df82000012e4011fece7b400000000000000000000000053595320f158d4546677b4795cc66dff59d154db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000343d3a424e422e424e423a626e62316879396136337467737668616d343633767732746b6b77326a35793230763564726877387032000000000000000000000000"); + EXPECT_EQ(hex(out), "0a010012010018012201002a0100422a307833626439323930366336306535383433636530316232646335346536646333353735623532313561524d0a4b0a080214e8348c4f0000123f3d3a424e422e424e423a626e623136676b3767637a73743539777938726e7872716e7433796e36663630757736656330773675763a3239373033393a743a30"); auto tx = Ethereum::Proto::SigningInput(); ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields - EXPECT_EQ(tx.to_address(), "0x8f66c4ae756bebc49ec8b81966dd8bba9f127549"); - ASSERT_TRUE(tx.transaction().has_contract_generic()); - - Data vaultAddressBin = SwapTest_ethAddressStringToData("0x53595320f158d4546677B4795Cc66dfF59D154Db"); - EXPECT_EQ(hex(vaultAddressBin), "53595320f158d4546677b4795cc66dff59d154db"); - auto func = Ethereum::ABI::Function("deposit", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(parse_hex("0000000000000000000000000000000000000000")), - std::make_shared(uint256_t(20000000000000000)), - std::make_shared("=:BNB.BNB:bnb1hy9a63tgsvham463vw2tkkw2j5y20v5drhw8p2")}); - Data payload; - func.encode(payload); - EXPECT_EQ(hex(payload), "1fece7b400000000000000000000000053595320f158d4546677b4795cc66dff59d154db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000343d3a424e422e424e423a626e62316879396136337467737668616d343633767732746b6b77326a35793230763564726877387032000000000000000000000000"); - EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "470de4df820000"); - EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); + EXPECT_EQ(tx.to_address(), "0x3bd92906c60e5843ce01b2dc54e6dc3575b5215a"); + ASSERT_FALSE(tx.transaction().has_contract_generic()); EXPECT_EQ(hex(TW::data(tx.private_key())), ""); // set few fields before signing auto chainId = store(uint256_t(43114)); tx.set_chain_id(chainId.data(), chainId.size()); - auto nonce = store(uint256_t(4)); + auto nonce = store(uint256_t(5)); tx.set_nonce(nonce.data(), nonce.size()); - auto gasPrice = store(uint256_t(30000000000)); - tx.set_gas_price(gasPrice.data(), gasPrice.size()); + auto maxInclusionFeePerGas = store(uint256_t(2000000000)); + auto maxFeePerGas = store(uint256_t(25000000000)); + tx.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + tx.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); auto gasLimit = store(uint256_t(108810)); tx.set_gas_limit(gasLimit.data(), gasLimit.size()); - auto privKey = parse_hex("42dcdf1cc2020a98f874a2f754eeb127e556ee62714973299e62ebdaadb48218"); + auto privKey = parse_hex("09da019c250b7e2b140645df36fd839806c5ae8eecf4d8f35e8ff57cf3bd1e57"); tx.set_private_key(privKey.data(), privKey.size()); // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeAvalancheCChain); - EXPECT_EQ(hex(output.encoded()), "f90154048506fc23ac008301a90a948f66c4ae756bebc49ec8b81966dd8bba9f12754987470de4df820000b8e41fece7b400000000000000000000000053595320f158d4546677b4795cc66dff59d154db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000343d3a424e422e424e423a626e62316879396136337467737668616d343633767732746b6b77326a35793230763564726877387032000000000000000000000000830150f8a09491c6b06fc1773bfc2a067787da96143f3d56eaf9fa9667298fdb298d33944ea04e2e2618e7c822b21af1ccc27746bfa46fa703b1c7f14cb0e33bd7f88acf2045"); + EXPECT_EQ(hex(output.encoded()), "02f8b682a86a0584773594008505d21dba008301a90a943bd92906c60e5843ce01b2dc54e6dc3575b5215a880214e8348c4f0000b83f3d3a424e422e424e423a626e623136676b3767637a73743539777938726e7872716e7433796e36663630757736656330773675763a3239373033393a743a30c001a06546e903b35a6a3704e2692cc80ecf49901edb9586fd28f5e084a1f334bad455a0277c2e122948df3b8efce3f7f2b73d5bc1f96c80bfbe5a45fe0914dc6fce5843"); // https://viewblock.io/thorchain/tx/8A29B132443BF1B0A0BD3E00F8155D10FEEEC7737BDC912C4A1AFB0A52E4FD4F // https://snowtrace.io/tx/0x8A29B132443BF1B0A0BD3E00F8155D10FEEEC7737BDC912C4A1AFB0A52E4FD4F // https://binance.mintscan.io/txs/9D250C8BAC8205B942A597AFB345045439A55CAB8DD588B75870D4E47D751C16 @@ -567,46 +557,25 @@ TEST(THORChainSwap, SwapEthBnb) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Eth) - .toAddress(Address1Bnb) - .vault(VaultEth) - .router(RouterEth) - .fromAmount("50000000000000000") - .toAmountLimit("600003") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Bnb) + .vault(VaultEth) + .fromAmount("50000000000000000") + .toAmountLimit("600003") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); - EXPECT_EQ(hex(out), "0a01001201002201002a0100422a30783432413545643435363635306130394463313045426336333631413734383066446436316632374252f30132f0010a07b1a2bc2ec5000012e4011fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030330000000000"); + EXPECT_EQ(hex(out), "0a010012010018012201002a0100422a30783130393163344465366133634630394364413030416244416544343263376333423639433833454352480a460a07b1a2bc2ec50000123b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033"); auto tx = Ethereum::Proto::SigningInput(); ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields - EXPECT_EQ(tx.to_address(), RouterEth); - ASSERT_TRUE(tx.transaction().has_contract_generic()); - - Data vaultAddressBin = SwapTest_ethAddressStringToData(VaultEth); - EXPECT_EQ(hex(vaultAddressBin), "1091c4de6a3cf09cda00abdaed42c7c3b69c83ec"); - auto func = Ethereum::ABI::Function("deposit", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(parse_hex("0000000000000000000000000000000000000000")), - std::make_shared(uint256_t(50000000000000000)), - std::make_shared("=:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:600003")}); - Data payload; - func.encode(payload); - EXPECT_EQ(hex(payload), "1fece7b4" - "0000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec" - "0000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000b1a2bc2ec50000" - "0000000000000000000000000000000000000000000000000000000000000080" - "000000000000000000000000000000000000000000000000000000000000003b" - "3d3a424e422e424e423a626e62317573343777646866783038636839377a6475" - "656833783375356d757266727833306a656372783a3630303030330000000000"); - EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "b1a2bc2ec50000"); - EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); + EXPECT_EQ(tx.to_address(), VaultEth); + ASSERT_FALSE(tx.transaction().has_contract_generic()); EXPECT_EQ(hex(TW::data(tx.private_key())), ""); @@ -625,7 +594,7 @@ TEST(THORChainSwap, SwapEthBnb) { // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeEthereum); - EXPECT_EQ(hex(output.encoded()), "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000000000026a0669563be8a0022fcd32fdf82ccca7dc66012ea28c57e95a2d9348dbf37afc377a03505f5eb041038c565d2f2888207c9dbcad8ca12f10ce5c5bd2ca41de01a9e89"); + EXPECT_EQ(hex(output.encoded()), "02f8a60103808083013880941091c4de6a3cf09cda00abdaed42c7c3b69c83ec87b1a2bc2ec50000b83b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033c080a00d605807f983650fafbfdcf0c33bdf0c524c7185eae8c1501ae24892faf16b1ba03b51b0a35e4754ab21d1e48fed635d8486048df50c253ba9af4cebdb6a92a450"); } TEST(THORChainSwap, SwapBnbBtc) { @@ -634,15 +603,15 @@ TEST(THORChainSwap, SwapBnbBtc) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::BTC)); toAsset.set_symbol("BTC"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Bnb) - .toAddress(Address1Btc) - .vault(VaultBnb) - .fromAmount("10000000") - .toAmountLimit("10000000") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress(Address1Btc) + .vault(VaultBnb) + .fromAmount("10000000") + .toAmountLimit("10000000") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); EXPECT_EQ(hex(out), "2a3d3d3a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a313030303030303052480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204"); @@ -675,15 +644,15 @@ TEST(THORChainSwap, SwapBnbEth) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::ETH)); toAsset.set_symbol("ETH"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Bnb) - .toAddress(Address1Eth) - .vault(VaultBnb) - .fromAmount("27000000") - .toAmountLimit("123456") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress(Address1Eth) + .vault(VaultBnb) + .fromAmount("27000000") + .toAmountLimit("123456") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); EXPECT_EQ(hex(out), "2a3b3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a31323334353652480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e4210c0f9ef0c12220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e4210c0f9ef0c"); @@ -723,15 +692,15 @@ TEST(THORChainSwap, SwapBnbRune) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::THOR)); toAsset.set_symbol("RUNE"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Bnb) - .toAddress(Address1Thor) - .vault(VaultBnb) - .fromAmount("4000000") - .toAmountLimit("121065076") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress(Address1Thor) + .vault(VaultBnb) + .fromAmount("4000000") + .toAmountLimit("121065076") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); EXPECT_EQ(hex(out), "2a413d3a54484f522e52554e453a74686f72317a3533777765376d64366365777a39737177717a6e306161767061756e3067773065786e32723a31323130363530373652480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e42108092f40112220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e42108092f401"); @@ -772,7 +741,7 @@ TEST(THORChainSwap, SwapBusdTokenBnb) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); - auto && [out, errorCode, error] = SwapBuilder::builder() + auto&& [out, errorCode, error] = SwapBuilder::builder() .from(fromAsset) .to(toAsset) .fromAddress("bnb1gddl87crh47wzynjx3c6pmcclzk7txlkm74x28") @@ -824,15 +793,15 @@ TEST(THORChainSwap, SwapBnbBnbToken) { toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); toAsset.set_token_id("TWT-8C2"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress("bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx") - .toAddress("bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx") - .vault("bnb1qefsjm654cdw94ejj8g4s49w7z8te75veslusz") - .fromAmount("10000000") - .toAmountLimit("5400000000") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress("bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx") + .toAddress("bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx") + .vault("bnb1qefsjm654cdw94ejj8g4s49w7z8te75veslusz") + .fromAmount("10000000") + .toAmountLimit("5400000000") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); EXPECT_EQ(hex(out), "2a433d3a424e422e5457542d3843323a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3534303030303030303052480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a140653096f54ae1ae2d73291d15854aef08ebcfa8c120a0a03424e421080ade204"); @@ -875,17 +844,17 @@ TEST(THORChainSwap, SwapBtcEthWithAffFee) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::ETH)); toAsset.set_symbol("ETH"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Btc) - .toAddress(Address1Eth) - .vault(VaultBtc) - .fromAmount("1000000") - .toAmountLimit("140000000000000000") - .affFeeAddress("thrnm") - .affFeeRate("10") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Eth) + .vault(VaultBtc) + .fromAmount("1000000") + .toAmountLimit("140000000000000000") + .affFeeAddress("thrnm") + .affFeeRate("10") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); EXPECT_EQ(hex(out), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130"); @@ -921,19 +890,19 @@ TEST(THORChainSwap, SwapBtcEthWithAffFee) { ANY_SIGN(tx, TWCoinTypeBitcoin); EXPECT_EQ(output.error(), 0); EXPECT_EQ(hex(output.encoded()), // printed using prettyPrintTransaction - "01000000" // version - "0001" // marker & flag - "01" // inputs - "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" - "03" // outputs - "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" - "0c9ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" - "0000000000000000" "53" "6a4c503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130" - // witness - "02" - "47" "3044022056e918d8dea9431057b7b8b7f7c990ff72d653aef296eda9a85e546537e1eaa4022050b64766ea4ce56ecd3325f184d67b20924fd4539cb40bbad916ede1cc26017f01" - "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" - "00000000" // nLockTime + "01000000" // version + "0001" // marker & flag + "01" // inputs + "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" + "03" // outputs + "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" + "0c9ceb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "53" "6a4c503d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a7468726e6d3a3130" + // witness + "02" + "47" "3044022056e918d8dea9431057b7b8b7f7c990ff72d653aef296eda9a85e546537e1eaa4022050b64766ea4ce56ecd3325f184d67b20924fd4539cb40bbad916ede1cc26017f01" + "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" + "00000000" // nLockTime ); } @@ -943,51 +912,27 @@ TEST(THORChainSwap, SwapEthBnbWithAffFee) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Eth) - .toAddress(Address1Bnb) - .vault(VaultEth) - .router(RouterEth) - .fromAmount("50000000000000000") - .toAmountLimit("600003") - .affFeeAddress("tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy") - .affFeeRate("10") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Bnb) + .vault(VaultEth) + .fromAmount("50000000000000000") + .toAmountLimit("600003") + .affFeeAddress("tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy") + .affFeeRate("10") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); - EXPECT_EQ(hex(out), "0a01001201002201002a0100422a30783432413545643435363635306130394463313045426336333631413734383066446436316632374252b30232b0020a07b1a2bc2ec5000012a4021fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130000000000000000000000000000000000000"); + EXPECT_EQ(hex(out), "0a010012010018012201002a0100422a307831303931633444653661336346303943644130304162444165443432633763334236394338334543527b0a790a07b1a2bc2ec50000126e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130"); auto tx = Ethereum::Proto::SigningInput(); ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields - EXPECT_EQ(tx.to_address(), RouterEth); - ASSERT_TRUE(tx.transaction().has_contract_generic()); - - Data vaultAddressBin = SwapTest_ethAddressStringToData(VaultEth); - EXPECT_EQ(hex(vaultAddressBin), "1091c4de6a3cf09cda00abdaed42c7c3b69c83ec"); - auto func = Ethereum::ABI::Function("deposit", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(parse_hex("0000000000000000000000000000000000000000")), - std::make_shared(uint256_t(50000000000000000)), - std::make_shared("=:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:600003:tthor1ql2tcqyrqsgnql2tcqyj2n8kfdmt9lh0yzql2tcqy:10")}); - Data payload; - func.encode(payload); - EXPECT_EQ(hex(payload), "1fece7b4" - "0000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec" - "0000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000b1a2bc2ec50000" - "0000000000000000000000000000000000000000000000000000000000000080" - "000000000000000000000000000000000000000000000000000000000000006e" - "3d3a424e422e424e423a626e62317573343777646866783038636839377a6475" - "656833783375356d757266727833306a656372783a3630303030333a7474686f" - "7231716c3274637179727173676e716c32746371796a326e386b66646d74396c" - "6830797a716c32746371793a3130000000000000000000000000000000000000"); - - EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "b1a2bc2ec50000"); - EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); + EXPECT_EQ(tx.to_address(), VaultEth); + ASSERT_FALSE(tx.transaction().has_contract_generic()); EXPECT_EQ(hex(TW::data(tx.private_key())), ""); @@ -1006,7 +951,7 @@ TEST(THORChainSwap, SwapEthBnbWithAffFee) { // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(tx, TWCoinTypeEthereum); - EXPECT_EQ(hex(output.encoded()), "f90192038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b901241fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a313000000000000000000000000000000000000026a0ee68bd41da9a9b1ad87fd547e83e4b8022460de024839f4f5f528abc6aecf2aea0402205812d62a075138743f6048ba2a1c073f4a3a14224009a34ee74d3dccef1"); + EXPECT_EQ(hex(output.encoded()), "02f8d90103808083013880941091c4de6a3cf09cda00abdaed42c7c3b69c83ec87b1a2bc2ec50000b86e3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030333a7474686f7231716c3274637179727173676e716c32746371796a326e386b66646d74396c6830797a716c32746371793a3130c001a05c16871b66fd0fa8f658d6f171310bab332d09e0533d6c97329a59ddc93a9a11a05ed2be94e6dbb640e58920c8be4fa597cd5f0a918123245acb899042dd43777f"); } TEST(THORChainSwap, SwapBtcNegativeMemoTooLong) { @@ -1015,18 +960,18 @@ TEST(THORChainSwap, SwapBtcNegativeMemoTooLong) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::ETH)); toAsset.set_symbol("ETH"); - auto && [out, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Btc) - .toAddress(Address1Eth) - .vault(VaultBtc) - .fromAmount("1000000") - .toAmountLimit("140000000000000000") - .affFeeAddress("affiliate_address") - .affFeeRate("10") - .extraMemo("extra_memo_very_loooooooooooooong") - .build(); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Eth) + .vault(VaultBtc) + .fromAmount("1000000") + .toAmountLimit("140000000000000000") + .affFeeAddress("affiliate_address") + .affFeeRate("10") + .extraMemo("extra_memo_very_loooooooooooooong") + .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); EXPECT_EQ(hex(out), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a7e3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030303030303030303a616666696c696174655f616464726573733a31303a65787472615f6d656d6f5f766572795f6c6f6f6f6f6f6f6f6f6f6f6f6f6f6f6e67"); @@ -1104,28 +1049,28 @@ TEST(THORChainSwap, WrongFromAddress) { toAsset.set_chain(static_cast(Chain::ETH)); toAsset.set_symbol("ETH"); { - auto && [_, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress("DummyAddress") - .toAddress(Address1Eth) - .vault(VaultEth) - .fromAmount("1000000") - .toAmountLimit("100000") - .build(); + auto&& [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress("DummyAddress") + .toAddress(Address1Eth) + .vault(VaultEth) + .fromAmount("1000000") + .toAmountLimit("100000") + .build(); EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_from_address); EXPECT_EQ(error, "Invalid from address"); } { - auto && [_, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Btc) - .toAddress(Address1Eth) - .vault(VaultEth) - .fromAmount("1000000") - .toAmountLimit("100000") - .build(); + auto&& [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Btc) + .toAddress(Address1Eth) + .vault(VaultEth) + .fromAmount("1000000") + .toAmountLimit("100000") + .build(); EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_from_address); EXPECT_EQ(error, "Invalid from address"); } @@ -1138,28 +1083,28 @@ TEST(THORChainSwap, WrongToAddress) { toAsset.set_chain(static_cast(Chain::ETH)); toAsset.set_symbol("ETH"); { - auto && [_, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Bnb) - .toAddress("DummyAddress") - .vault(VaultEth) - .fromAmount("100000") - .toAmountLimit("100000") - .build(); + auto&& [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress("DummyAddress") + .vault(VaultEth) + .fromAmount("100000") + .toAmountLimit("100000") + .build(); EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_to_address); EXPECT_EQ(error, "Invalid to address"); } { - auto && [_, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Bnb) - .toAddress(Address1Btc) - .vault(VaultEth) - .fromAmount("100000") - .toAmountLimit("100000") - .build(); + auto&& [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Bnb) + .toAddress(Address1Btc) + .vault(VaultEth) + .fromAmount("100000") + .toAmountLimit("100000") + .build(); EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_to_address); EXPECT_EQ(error, "Invalid to address"); } @@ -1171,14 +1116,14 @@ TEST(THORChainSwap, FromRuneNotSupported) { Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); - auto && [_, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Thor) - .toAddress(Address1Bnb) - .fromAmount("1000") - .toAmountLimit("1000") - .build(); + auto&& [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Thor) + .toAddress(Address1Bnb) + .fromAmount("1000") + .toAmountLimit("1000") + .build(); EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Unsupported_from_chain); EXPECT_EQ(error, "Unsupported from chain: 0"); } @@ -1190,30 +1135,31 @@ TEST(THORChainSwap, EthInvalidVault) { toAsset.set_chain(static_cast(Chain::BNB)); toAsset.set_symbol("BNB"); { - auto && [_, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Eth) - .toAddress(Address1Bnb) - .vault("_INVALID_ADDRESS_") - .router(RouterEth) - .fromAmount("50000000000000000") - .toAmountLimit("600003") - .build(); + fromAsset.set_token_id("0x53595320f158d4546677b4795cc66dff59d154db"); + auto&& [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Bnb) + .vault("_INVALID_ADDRESS_") + .router(RouterEth) + .fromAmount("50000000000000000") + .toAmountLimit("600003") + .build(); EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_vault_address); EXPECT_EQ(error, "Invalid vault address: _INVALID_ADDRESS_"); } { - auto && [_, errorCode, error] = SwapBuilder::builder() - .from(fromAsset) - .to(toAsset) - .fromAddress(Address1Eth) - .toAddress(Address1Bnb) - .vault(VaultEth) - .router("_INVALID_ADDRESS_") - .fromAmount("50000000000000000") - .toAmountLimit("600003") - .build(); + auto&& [_, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Bnb) + .vault(VaultEth) + .router("_INVALID_ADDRESS_") + .fromAmount("50000000000000000") + .toAmountLimit("600003") + .build(); EXPECT_EQ(errorCode, Proto::ErrorCode::Error_Invalid_router_address); EXPECT_EQ(error, "Invalid router address: _INVALID_ADDRESS_"); } diff --git a/tests/chains/THORChain/TWSwapTests.cpp b/tests/chains/THORChain/TWSwapTests.cpp index 3d46e960f03..ce5f215241a 100644 --- a/tests/chains/THORChain/TWSwapTests.cpp +++ b/tests/chains/THORChain/TWSwapTests.cpp @@ -144,7 +144,7 @@ TEST(TWTHORChainSwap, SwapEthBnb) { // invoke swap const auto outputTWData_ = WRAPD(TWTHORChainSwapBuildSwap(inputTWData_.get())); const auto outputData = data(TWDataBytes(outputTWData_.get()), TWDataSize(outputTWData_.get())); - EXPECT_EQ(outputData.size(), 311ul); + EXPECT_EQ(outputData.size(), 141ul); // parse result in proto Proto::SwapOutput outputProto; EXPECT_TRUE(outputProto.ParseFromArray(outputData.data(), static_cast(outputData.size()))); @@ -171,7 +171,7 @@ TEST(TWTHORChainSwap, SwapEthBnb) { // sign and encode resulting input Ethereum::Proto::SigningOutput output; ANY_SIGN(txInput, TWCoinTypeEthereum); - EXPECT_EQ(hex(output.encoded()), "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000000000026a0669563be8a0022fcd32fdf82ccca7dc66012ea28c57e95a2d9348dbf37afc377a03505f5eb041038c565d2f2888207c9dbcad8ca12f10ce5c5bd2ca41de01a9e89"); + EXPECT_EQ(hex(output.encoded()), "02f8a60103808083013880941091c4de6a3cf09cda00abdaed42c7c3b69c83ec87b1a2bc2ec50000b83b3d3a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033c080a00d605807f983650fafbfdcf0c33bdf0c524c7185eae8c1501ae24892faf16b1ba03b51b0a35e4754ab21d1e48fed635d8486048df50c253ba9af4cebdb6a92a450"); } TEST(TWTHORChainSwap, SwapBnbBtc) { From 299f9047e06d96783ce6d579690727836e892355 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 18 Jan 2023 09:05:43 +0100 Subject: [PATCH 079/426] feat(swift): update Package.swift (#2879) --- Package.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 7448baba099..0fdcdb93fe9 100644 --- a/Package.swift +++ b/Package.swift @@ -12,13 +12,13 @@ let package = Package( targets: [ .binaryTarget( name: "WalletCore", - url: "https://github.com/trustwallet/wallet-core/releases/download/3.1.0/WalletCore.xcframework.zip", - checksum: "2487af30a8f6f4775a41f29456f792f15269f4f4daae2da7e4b9ef8747613e3a" + url: "https://github.com/trustwallet/wallet-core/releases/download/3.1.8/WalletCore.xcframework.zip", + checksum: "ce7e45a8eea666dacc5cd9076f597e4f824c030299414c7fdd91e71b29aa09cc" ), .binaryTarget( name: "SwiftProtobuf", - url: "https://github.com/trustwallet/wallet-core/releases/download/3.1.0/SwiftProtobuf.xcframework.zip", - checksum: "2038b1d43c9f6aeb4957e3763382b8558983a1a811b168dca71be3636bb8350a" + url: "https://github.com/trustwallet/wallet-core/releases/download/3.1.8/SwiftProtobuf.xcframework.zip", + checksum: "222a306b49d7733b9c5d790b07c0d8240f68f27315f1d127aa4ed743b1b11920" ) ] ) From 0257de2dd4ba03e26a49e365871aa2c71ddd0a38 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 18 Jan 2023 09:09:23 +0100 Subject: [PATCH 080/426] [EIP1014]: eip1014 support (#2873) --- .../ethereum/TestEthereumAddress.kt | 12 ++ .../core/app/utils/TestHDWallet.kt | 2 +- .../{TWEthereumEip2645.h => TWEthereum.h} | 14 ++- src/Ethereum/EIP1014.cpp | 34 ++++++ src/Ethereum/EIP1014.h | 16 +++ .../{TWEthereumEip2645.cpp => TWEthereum.cpp} | 13 +- swift/Tests/Blockchains/EthereumTests.swift | 8 ++ swift/Tests/HDWalletTests.swift | 2 +- tests/chains/Ethereum/EIP1014Tests.cpp | 113 ++++++++++++++++++ tests/interface/TWHDWalletTests.cpp | 2 +- 10 files changed, 209 insertions(+), 7 deletions(-) rename include/TrustWalletCore/{TWEthereumEip2645.h => TWEthereum.h} (57%) create mode 100644 src/Ethereum/EIP1014.cpp create mode 100644 src/Ethereum/EIP1014.h rename src/interface/{TWEthereumEip2645.cpp => TWEthereum.cpp} (57%) create mode 100644 tests/chains/Ethereum/EIP1014Tests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt index f4b29c5db5d..c860c1dbaac 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt @@ -1,10 +1,13 @@ package com.trustwallet.core.app.blockchains.ethereum +import com.trustwallet.core.app.utils.Numeric import org.junit.Assert.assertEquals import org.junit.Test import wallet.core.jni.AnyAddress import wallet.core.jni.CoinType import org.junit.Assert.assertFalse +import wallet.core.jni.Ethereum +import wallet.core.jni.Hash class TestEthereumAddress { @@ -12,6 +15,15 @@ class TestEthereumAddress { System.loadLibrary("TrustWalletCore") } + @Test + fun testEthereumCreate2Addresses() { + val from = "0x0000000000000000000000000000000000000000" + val salt = Numeric.hexStringToByteArray("0x0000000000000000000000000000000000000000000000000000000000000000") + val initCodeHash = Hash.keccak256(Numeric.hexStringToByteArray("0x0")) + val result = Ethereum.eip1014AddressCreate2(from, salt, initCodeHash) + assertEquals(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38") + } + @Test fun testEthereumAddresses() { val any = AnyAddress("0x7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1", CoinType.ETHEREUM) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt index 07c91d0829b..bd0faaef521 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt @@ -29,7 +29,7 @@ class TestHDWallet { fun testCreateFromMnemonicImmutableXMainnetFromSignature() { // Successfully register: https://api.x.immutable.com/v1/users/0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37 val hd = HDWallet("obscure opera favorite shuffle mail tip age debate dirt pact cement loyal", "") - val derivationPath = EthereumEip2645.getPath("0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37", "starkex", "immutablex", "1") + val derivationPath = Ethereum.eip2645GetPath("0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37", "starkex", "immutablex", "1") assertEquals(derivationPath, "m/2645'/579218131'/211006541'/2124474935'/1609799702'/1") // Retrieve eth private key diff --git a/include/TrustWalletCore/TWEthereumEip2645.h b/include/TrustWalletCore/TWEthereum.h similarity index 57% rename from include/TrustWalletCore/TWEthereumEip2645.h rename to include/TrustWalletCore/TWEthereum.h index 95678fa8be6..3da4511af74 100644 --- a/include/TrustWalletCore/TWEthereumEip2645.h +++ b/include/TrustWalletCore/TWEthereum.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2022 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,12 +7,22 @@ #pragma once #include "TWBase.h" +#include "TWData.h" #include "TWString.h" TW_EXTERN_C_BEGIN TW_EXPORT_STRUCT -struct TWEthereumEip2645; +struct TWEthereum; + +/// EIP-1014: Skinny CREATE2 (guess smart contract create2 address) +/// +/// \param fromEthAddress valid eth address +/// \param salt always 32 bytes stack item +/// \param initCodeHash The init_code is the code that, when executed, produces the runtime bytecode that will be placed into the state, and which typically is used by high level languages to implement a ‘constructor’. Need to be provided hashed with keccak256 +/// \return Ethereum resulting address +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWEthereumEip1014AddressCreate2(TWString* _Nonnull fromEthAddress, TWData* _Nonnull salt, TWData* _Nonnull initCodeHash); /// Generate a layer 2 eip2645 derivation path from eth address, layer, application and given index. /// diff --git a/src/Ethereum/EIP1014.cpp b/src/Ethereum/EIP1014.cpp new file mode 100644 index 00000000000..4242126a8d1 --- /dev/null +++ b/src/Ethereum/EIP1014.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2023 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 "EIP1014.h" +#include "AddressChecksum.h" +#include "Hash.h" +#include "HexCoding.h" + +namespace TW::Ethereum { + +Data create2Address(const std::string& from, const Data& salt, const Data& initCodeHash) { + if (salt.size() != 32) { + throw std::runtime_error("Error: salt must be 32 bytes."); + } + if (initCodeHash.size() != 32) { + throw std::runtime_error("Error: initCodeHash must be 32 bytes."); + } + Data input = {0xff}; + append(input, parse_hex(from)); + append(input, salt); + append(input, initCodeHash); + auto hash = Hash::keccak256(input); + return Data(hash.end() - 20, hash.end()); +} + +std::string create2AddressString(const std::string& from, const Data& salt, const Data& initCodeHash) { + auto addressData = create2Address(from, salt, initCodeHash); + return Ethereum::checksumed(Address(hexEncoded(addressData))); +} + +} // namespace TW::Ethereum diff --git a/src/Ethereum/EIP1014.h b/src/Ethereum/EIP1014.h new file mode 100644 index 00000000000..d1bde394e22 --- /dev/null +++ b/src/Ethereum/EIP1014.h @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 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" + +namespace TW::Ethereum { + +Data create2Address(const std::string& from, const Data& salt, const Data& initCodeHash); +std::string create2AddressString(const std::string& from, const Data& salt, const Data& initCodeHash); + +} diff --git a/src/interface/TWEthereumEip2645.cpp b/src/interface/TWEthereum.cpp similarity index 57% rename from src/interface/TWEthereumEip2645.cpp rename to src/interface/TWEthereum.cpp index 21a9082573a..e08c0f12c79 100644 --- a/src/interface/TWEthereumEip2645.cpp +++ b/src/interface/TWEthereum.cpp @@ -1,14 +1,23 @@ -// Copyright © 2017-2022 Trust Wallet. +// Copyright © 2017-2023 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 +#include "Data.h" +#include "Ethereum/EIP1014.h" #include "Ethereum/EIP2645.h" +#include #include +TWString* TWEthereumEip1014AddressCreate2(TWString* _Nonnull fromEthAddress, TWData* _Nonnull salt, TWData* _Nonnull initCodeHash) { + const auto& ethAddressStr = *reinterpret_cast(fromEthAddress); + const auto& saltData = *reinterpret_cast(salt); + const auto& initCodeHashData = *reinterpret_cast(initCodeHash); + return new std::string(TW::Ethereum::create2AddressString(ethAddressStr, saltData, initCodeHashData)); +} + TWString* TWEthereumEip2645GetPath(TWString* ethAddress, TWString* layer, TWString* application, TWString* index) { const auto& ethAddressStr = *reinterpret_cast(ethAddress); const auto& layerStr = *reinterpret_cast(layer); diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index e53e1cdebb1..1409816761f 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -9,6 +9,14 @@ import WalletCore class EthereumTests: XCTestCase { + func testCreate2Address() { + let address = "0x0000000000000000000000000000000000000000" + let salt = Data(hexString: "0x0000000000000000000000000000000000000000000000000000000000000000")! + let initCodeHash = Hash.keccak256(data: Data(hexString: "0x00")!) + let result = Ethereum.eip1014AddressCreate2(fromEthAddress: address, salt: salt, initCodeHash: initCodeHash) + XCTAssertEqual(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38") + } + func testAddress() { let anyAddress = AnyAddress(string: "0x7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1", coin: .ethereum) diff --git a/swift/Tests/HDWalletTests.swift b/swift/Tests/HDWalletTests.swift index 6dccac4705f..537f8b0a961 100644 --- a/swift/Tests/HDWalletTests.swift +++ b/swift/Tests/HDWalletTests.swift @@ -15,7 +15,7 @@ class HDWalletTests: XCTestCase { func testFromMnemonicImmutableXMainnetFromSignature() { let wallet = HDWallet(mnemonic: "obscure opera favorite shuffle mail tip age debate dirt pact cement loyal", passphrase: "")! - let starkDerivationPath = EthereumEip2645.getPath(ethAddress: "0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37", layer: "starkex", application: "immutablex", index: "1") + let starkDerivationPath = Ethereum.eip2645GetPath(ethAddress: "0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37", layer: "starkex", application: "immutablex", index: "1") XCTAssertEqual(starkDerivationPath, "m/2645'/579218131'/211006541'/2124474935'/1609799702'/1") // Retrieve eth private key diff --git a/tests/chains/Ethereum/EIP1014Tests.cpp b/tests/chains/Ethereum/EIP1014Tests.cpp new file mode 100644 index 00000000000..2a9e38864bd --- /dev/null +++ b/tests/chains/Ethereum/EIP1014Tests.cpp @@ -0,0 +1,113 @@ +// Copyright © 2017-2023 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 "TestUtilities.h" +#include +#include +#include +#include +#include +#include + +#include + +namespace TW::Ethereum::tests { + TEST(EthereumEip1014, Example0) { + // C++ + { + const std::string& from = "0x0000000000000000000000000000000000000000"; + const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); + Data initCodeHash = Hash::keccak256(parse_hex("0x00")); + const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"); + } + + // C + { + const auto from = STRING("0x0000000000000000000000000000000000000000"); + const auto salt = DATA("0x0000000000000000000000000000000000000000000000000000000000000000"); + const auto initCodeHash = WRAPD(TWHashKeccak256(DATA("0x00").get())); + const auto& result = WRAPS(TWEthereumEip1014AddressCreate2(from.get(), salt.get(), initCodeHash.get())); + assertStringsEqual(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"); + } + } + + TEST(EthereumEip1014, Example1) { + // C++ + { + const std::string& from = "0xdeadbeef00000000000000000000000000000000"; + const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); + Data initCodeHash = Hash::keccak256(parse_hex("0x00")); + const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3"); + } + + // C + { + const auto from = STRING("0xdeadbeef00000000000000000000000000000000"); + const auto salt = DATA("0x0000000000000000000000000000000000000000000000000000000000000000"); + const auto initCodeHash = WRAPD(TWHashKeccak256(DATA("0x00").get())); + const auto& result = WRAPS(TWEthereumEip1014AddressCreate2(from.get(), salt.get(), initCodeHash.get())); + assertStringsEqual(result, "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3"); + } + } + + TEST(EthereumEip1014, Example2) { + const std::string& from = "0xdeadbeef00000000000000000000000000000000"; + const Data salt = parse_hex("0x000000000000000000000000feed000000000000000000000000000000000000"); + Data initCodeHash = Hash::keccak256(parse_hex("0x00")); + const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0xD04116cDd17beBE565EB2422F2497E06cC1C9833"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0xD04116cDd17beBE565EB2422F2497E06cC1C9833"); + } + + TEST(EthereumEip1014, Example3) { + const std::string& from = "0x0000000000000000000000000000000000000000"; + const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); + Data initCodeHash = Hash::keccak256(parse_hex("0xdeadbeef")); + const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e"); + } + + TEST(EthereumEip1014, Example4) { + const std::string& from = "0x00000000000000000000000000000000deadbeef"; + const Data salt = parse_hex("0x00000000000000000000000000000000000000000000000000000000cafebabe"); + Data initCodeHash = Hash::keccak256(parse_hex("0xdeadbeef")); + const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7"); + } + + TEST(EthereumEip1014, Example5) { + const std::string& from = "0x00000000000000000000000000000000deadbeef"; + const Data salt = parse_hex("0x00000000000000000000000000000000000000000000000000000000cafebabe"); + Data initCodeHash = Hash::keccak256(parse_hex("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C"); + } + + TEST(EthereumEip1014, Example6) { + const std::string& from = "0x0000000000000000000000000000000000000000"; + const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); + Data initCodeHash = Hash::keccak256(parse_hex("0x")); + const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0"); + } + + TEST(EthereumEip1014, Example7) { + const std::string& from = "0x7EF2e0048f5bAeDe046f6BF797943daF4ED8CB47"; + const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); + Data initCodeHash = Hash::keccak256(parse_hex("0x608060405260405162000c5138038062000c51833981810160405281019062000029919062000580565b6200003d828260006200004560201b60201c565b5050620007d7565b62000056836200008860201b60201c565b600082511180620000645750805b156200008357620000818383620000df60201b620000371760201c565b505b505050565b62000099816200011560201b60201c565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b60606200010d838360405180606001604052806027815260200162000c2a60279139620001eb60201b60201c565b905092915050565b6200012b816200027d60201b620000641760201c565b6200016d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000164906200066d565b60405180910390fd5b80620001a77f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b620002a060201b620000871760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60606000808573ffffffffffffffffffffffffffffffffffffffff1685604051620002179190620006dc565b600060405180830381855af49150503d806000811462000254576040519150601f19603f3d011682016040523d82523d6000602084013e62000259565b606091505b50915091506200027286838387620002aa60201b60201c565b925050509392505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b606083156200031a5760008351036200031157620002ce856200027d60201b60201c565b62000310576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620003079062000745565b60405180910390fd5b5b8290506200032d565b6200032c83836200033560201b60201c565b5b949350505050565b600082511115620003495781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200037f9190620007b3565b60405180910390fd5b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620003c9826200039c565b9050919050565b620003db81620003bc565b8114620003e757600080fd5b50565b600081519050620003fb81620003d0565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000456826200040b565b810181811067ffffffffffffffff821117156200047857620004776200041c565b5b80604052505050565b60006200048d62000388565b90506200049b82826200044b565b919050565b600067ffffffffffffffff821115620004be57620004bd6200041c565b5b620004c9826200040b565b9050602081019050919050565b60005b83811015620004f6578082015181840152602081019050620004d9565b60008484015250505050565b6000620005196200051384620004a0565b62000481565b90508281526020810184848401111562000538576200053762000406565b5b62000545848285620004d6565b509392505050565b600082601f83011262000565576200056462000401565b5b81516200057784826020860162000502565b91505092915050565b600080604083850312156200059a576200059962000392565b5b6000620005aa85828601620003ea565b925050602083015167ffffffffffffffff811115620005ce57620005cd62000397565b5b620005dc858286016200054d565b9150509250929050565b600082825260208201905092915050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b600062000655602d83620005e6565b91506200066282620005f7565b604082019050919050565b60006020820190508181036000830152620006888162000646565b9050919050565b600081519050919050565b600081905092915050565b6000620006b2826200068f565b620006be81856200069a565b9350620006d0818560208601620004d6565b80840191505092915050565b6000620006ea8284620006a5565b915081905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006200072d601d83620005e6565b91506200073a82620006f5565b602082019050919050565b6000602082019050818103600083015262000760816200071e565b9050919050565b600081519050919050565b60006200077f8262000767565b6200078b8185620005e6565b93506200079d818560208601620004d6565b620007a8816200040b565b840191505092915050565b60006020820190508181036000830152620007cf818462000772565b905092915050565b61044380620007e76000396000f3fe6080604052366100135761001161001d565b005b61001b61001d565b005b610025610091565b610035610030610093565b6100a2565b565b606061005c83836040518060600160405280602781526020016103e7602791396100c8565b905092915050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b565b600061009d61014e565b905090565b3660008037600080366000845af43d6000803e80600081146100c3573d6000f35b3d6000fd5b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516100f291906102db565b600060405180830381855af49150503d806000811461012d576040519150601f19603f3d011682016040523d82523d6000602084013e610132565b606091505b5091509150610143868383876101a5565b925050509392505050565b600061017c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b610087565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606083156102075760008351036101ff576101bf85610064565b6101fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f59061034f565b60405180910390fd5b5b829050610212565b610211838361021a565b5b949350505050565b60008251111561022d5781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161026191906103c4565b60405180910390fd5b600081519050919050565b600081905092915050565b60005b8381101561029e578082015181840152602081019050610283565b60008484015250505050565b60006102b58261026a565b6102bf8185610275565b93506102cf818560208601610280565b80840191505092915050565b60006102e782846102aa565b915081905092915050565b600082825260208201905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b6000610339601d836102f2565b915061034482610303565b602082019050919050565b600060208201905081810360008301526103688161032c565b9050919050565b600081519050919050565b6000601f19601f8301169050919050565b60006103968261036f565b6103a081856102f2565b93506103b0818560208601610280565b6103b98161037a565b840191505092915050565b600060208201905081810360008301526103de818461038b565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e57dd3eafc9985be746025b6d82d4f011b9a7bb3db56f9a1eb7eadfddd376b6064736f6c63430008110033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564000000000000000000000000d9ec9e840bb5df076dbbb488d01485058f421e5800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000be8fa0112dcb7d21dc63645b633073651e19934800000000000000000000000000000000000000000000000000000000")); + const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x4455e5f0038795939c001aa4d296A45956C460AA"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x4455e5f0038795939c001aa4d296A45956C460AA"); + } +} diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index 9cbf538898b..f97644e1ad5 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include From eb92d06340613d63b1705ffb3b0a137271647b73 Mon Sep 17 00:00:00 2001 From: safocl Date: Mon, 23 Jan 2023 17:58:23 +0400 Subject: [PATCH 081/426] fixed error of the [array with mismatched bound] in trezor-crypto/crypto/sha2.c (#2884) --- trezor-crypto/crypto/sha2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trezor-crypto/crypto/sha2.c b/trezor-crypto/crypto/sha2.c index bea30dae7cf..47d7eebbcdb 100644 --- a/trezor-crypto/crypto/sha2.c +++ b/trezor-crypto/crypto/sha2.c @@ -589,7 +589,7 @@ void sha1_Update(SHA1_CTX* context, const sha2_byte *data, size_t len) { usedspace = freespace = 0; } -void sha1_Final(SHA1_CTX* context, sha2_byte digest[]) { +void sha1_Final(SHA1_CTX* context, sha2_byte digest[SHA1_DIGEST_LENGTH]) { unsigned int usedspace = 0; /* If no digest buffer is passed, we don't bother doing this: */ @@ -896,7 +896,7 @@ void sha256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) { usedspace = freespace = 0; } -void sha256_Final(SHA256_CTX* context, sha2_byte digest[]) { +void sha256_Final(SHA256_CTX* context, sha2_byte digest[SHA256_DIGEST_LENGTH]) { unsigned int usedspace = 0; /* If no digest buffer is passed, we don't bother doing this: */ @@ -1250,7 +1250,7 @@ static void sha512_Last(SHA512_CTX* context) { sha512_Transform(context->state, context->buffer, context->state); } -void sha512_Final(SHA512_CTX* context, sha2_byte digest[]) { +void sha512_Final(SHA512_CTX* context, sha2_byte digest[SHA512_DIGEST_LENGTH]) { /* If no digest buffer is passed, we don't bother doing this: */ if (digest != (sha2_byte*)0) { sha512_Last(context); From 2f0be459b4acb107547ec9624bc02a570aa07a9c Mon Sep 17 00:00:00 2001 From: Ruslan Serebriakov Date: Tue, 24 Jan 2023 05:53:15 +0000 Subject: [PATCH 082/426] [EIP4337]: deployment address calculation (#2885) --- .../ethereum/TestEthereumAddress.kt | 9 ++++ include/TrustWalletCore/TWEthereum.h | 8 ++++ src/Ethereum/EIP1967.cpp | 31 ++++++++++++ src/Ethereum/EIP1967.h | 15 ++++++ src/Ethereum/EIP4337.cpp | 34 ++++++++++++++ src/Ethereum/EIP4337.h | 16 +++++++ src/interface/TWEthereum.cpp | 8 ++++ swift/Tests/Blockchains/EthereumTests.swift | 8 ++++ tests/chains/Ethereum/EIP1967Tests.cpp | 28 +++++++++++ tests/chains/Ethereum/EIP4337Tests.cpp | 47 +++++++++++++++++++ 10 files changed, 204 insertions(+) create mode 100644 src/Ethereum/EIP1967.cpp create mode 100644 src/Ethereum/EIP1967.h create mode 100644 src/Ethereum/EIP4337.cpp create mode 100644 src/Ethereum/EIP4337.h create mode 100644 tests/chains/Ethereum/EIP1967Tests.cpp create mode 100644 tests/chains/Ethereum/EIP4337Tests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt index c860c1dbaac..3089d9bd510 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt @@ -32,4 +32,13 @@ class TestEthereumAddress { assertFalse(AnyAddress.isValid("0xMQqpqMQgCBuiPkoXfgZZsJvuzCeI1zc00z6vHJj4", CoinType.ETHEREUM)) } + + @Test + fun testEthereumEIP4337DeploymentAddress() { + val factoryAddress = "0xd9145CCE52D386f254917e481eB44e9943F39138" + val logicAddress = "0x5C9eb5D6a6C2c1B3EFc52255C0b356f116f6f66D" + val ownerAddress = "0xA5a1dddEF094095AfB7b6e322dE72961DF2e1988" + val result = Ethereum.eip4337GetDeploymentAddress(factoryAddress, logicAddress, ownerAddress) + assertEquals(result, "0xbEaA87cEEaC906C21aaacd258FbFB87CfA3c90a8") + } } diff --git a/include/TrustWalletCore/TWEthereum.h b/include/TrustWalletCore/TWEthereum.h index 3da4511af74..9fab0ddba90 100644 --- a/include/TrustWalletCore/TWEthereum.h +++ b/include/TrustWalletCore/TWEthereum.h @@ -35,4 +35,12 @@ TWString* _Nonnull TWEthereumEip1014AddressCreate2(TWString* _Nonnull fromEthAdd TW_EXPORT_STATIC_METHOD TWString* _Nonnull TWEthereumEip2645GetPath(TWString* _Nonnull ethAddress, TWString* _Nonnull layer, TWString* _Nonnull application, TWString* _Nonnull index); +/// Generates a deployment address for a ERC-4337 compatible smart contract wallet +/// +/// \param factoryAddress non-null address of the account factory +/// \param logicAddress non-null address of the wallet's logic smart contract +/// \param ownerAddress non-null address of the signing key that controls the smart contract wallet +/// \return Ethereum resulting address +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWEthereumEip4337GetDeploymentAddress(TWString* _Nonnull factoryAddress, TWString* _Nonnull logicAddress, TWString* _Nonnull ownerAddress); TW_EXTERN_C_END diff --git a/src/Ethereum/EIP1967.cpp b/src/Ethereum/EIP1967.cpp new file mode 100644 index 00000000000..55a6907d8f1 --- /dev/null +++ b/src/Ethereum/EIP1967.cpp @@ -0,0 +1,31 @@ +// Copyright © 2017-2023 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 "EIP1014.h" +#include "AddressChecksum.h" +#include "Hash.h" +#include "HexCoding.h" +#include +#include "ABI.h" + +namespace TW::Ethereum { + +static const std::string eip1967ProxyBytecodeHex = R"(0x608060405260405162000c5138038062000c51833981810160405281019062000029919062000580565b6200003d828260006200004560201b60201c565b5050620007d7565b62000056836200008860201b60201c565b600082511180620000645750805b156200008357620000818383620000df60201b620000371760201c565b505b505050565b62000099816200011560201b60201c565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b60606200010d838360405180606001604052806027815260200162000c2a60279139620001eb60201b60201c565b905092915050565b6200012b816200027d60201b620000641760201c565b6200016d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000164906200066d565b60405180910390fd5b80620001a77f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b620002a060201b620000871760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60606000808573ffffffffffffffffffffffffffffffffffffffff1685604051620002179190620006dc565b600060405180830381855af49150503d806000811462000254576040519150601f19603f3d011682016040523d82523d6000602084013e62000259565b606091505b50915091506200027286838387620002aa60201b60201c565b925050509392505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b606083156200031a5760008351036200031157620002ce856200027d60201b60201c565b62000310576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620003079062000745565b60405180910390fd5b5b8290506200032d565b6200032c83836200033560201b60201c565b5b949350505050565b600082511115620003495781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200037f9190620007b3565b60405180910390fd5b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620003c9826200039c565b9050919050565b620003db81620003bc565b8114620003e757600080fd5b50565b600081519050620003fb81620003d0565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000456826200040b565b810181811067ffffffffffffffff821117156200047857620004776200041c565b5b80604052505050565b60006200048d62000388565b90506200049b82826200044b565b919050565b600067ffffffffffffffff821115620004be57620004bd6200041c565b5b620004c9826200040b565b9050602081019050919050565b60005b83811015620004f6578082015181840152602081019050620004d9565b60008484015250505050565b6000620005196200051384620004a0565b62000481565b90508281526020810184848401111562000538576200053762000406565b5b62000545848285620004d6565b509392505050565b600082601f83011262000565576200056462000401565b5b81516200057784826020860162000502565b91505092915050565b600080604083850312156200059a576200059962000392565b5b6000620005aa85828601620003ea565b925050602083015167ffffffffffffffff811115620005ce57620005cd62000397565b5b620005dc858286016200054d565b9150509250929050565b600082825260208201905092915050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b600062000655602d83620005e6565b91506200066282620005f7565b604082019050919050565b60006020820190508181036000830152620006888162000646565b9050919050565b600081519050919050565b600081905092915050565b6000620006b2826200068f565b620006be81856200069a565b9350620006d0818560208601620004d6565b80840191505092915050565b6000620006ea8284620006a5565b915081905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006200072d601d83620005e6565b91506200073a82620006f5565b602082019050919050565b6000602082019050818103600083015262000760816200071e565b9050919050565b600081519050919050565b60006200077f8262000767565b6200078b8185620005e6565b93506200079d818560208601620004d6565b620007a8816200040b565b840191505092915050565b60006020820190508181036000830152620007cf818462000772565b905092915050565b61044380620007e76000396000f3fe6080604052366100135761001161001d565b005b61001b61001d565b005b610025610091565b610035610030610093565b6100a2565b565b606061005c83836040518060600160405280602781526020016103e7602791396100c8565b905092915050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b565b600061009d61014e565b905090565b3660008037600080366000845af43d6000803e80600081146100c3573d6000f35b3d6000fd5b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516100f291906102db565b600060405180830381855af49150503d806000811461012d576040519150601f19603f3d011682016040523d82523d6000602084013e610132565b606091505b5091509150610143868383876101a5565b925050509392505050565b600061017c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b610087565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606083156102075760008351036101ff576101bf85610064565b6101fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f59061034f565b60405180910390fd5b5b829050610212565b610211838361021a565b5b949350505050565b60008251111561022d5781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161026191906103c4565b60405180910390fd5b600081519050919050565b600081905092915050565b60005b8381101561029e578082015181840152602081019050610283565b60008484015250505050565b60006102b58261026a565b6102bf8185610275565b93506102cf818560208601610280565b80840191505092915050565b60006102e782846102aa565b915081905092915050565b600082825260208201905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b6000610339601d836102f2565b915061034482610303565b602082019050919050565b600060208201905081810360008301526103688161032c565b9050919050565b600081519050919050565b6000601f19601f8301169050919050565b60006103968261036f565b6103a081856102f2565b93506103b0818560208601610280565b6103b98161037a565b840191505092915050565b600060208201905081810360008301526103de818461038b565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e57dd3eafc9985be746025b6d82d4f011b9a7bb3db56f9a1eb7eadfddd376b6064736f6c63430008110033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564)"; + +Data getEIP1967ProxyInitCode(const std::string& logicAddress, const Data& data) { + Data initCode = parse_hex(eip1967ProxyBytecodeHex); + + auto proxyConstructorParams = ABI::ParamTuple(); + proxyConstructorParams.addParam(std::make_shared(parse_hex(logicAddress))); + proxyConstructorParams.addParam(std::make_shared(data)); + Data proxyConstructorParamsEncoded; + proxyConstructorParams.encode(proxyConstructorParamsEncoded); + + append(initCode, proxyConstructorParamsEncoded); + return initCode; +} + +} // namespace TW::Ethereum diff --git a/src/Ethereum/EIP1967.h b/src/Ethereum/EIP1967.h new file mode 100644 index 00000000000..b9b65210f97 --- /dev/null +++ b/src/Ethereum/EIP1967.h @@ -0,0 +1,15 @@ +// Copyright © 2017-2023 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" + +namespace TW::Ethereum { + +Data getEIP1967ProxyInitCode(const std::string& logicAddress, const Data& data); + +} diff --git a/src/Ethereum/EIP4337.cpp b/src/Ethereum/EIP4337.cpp new file mode 100644 index 00000000000..dd770fc16e5 --- /dev/null +++ b/src/Ethereum/EIP4337.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2023 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 "EIP1014.h" +#include "EIP1967.h" +#include "AddressChecksum.h" +#include "Hash.h" +#include "HexCoding.h" +#include +#include "ABI.h" + +namespace TW::Ethereum { + +Data getEIP4337LogicInitializeBytecode(const std::string& ownerAddress) { + auto initializeFunc = ABI::Function("initialize", std::vector>{ + std::make_shared(parse_hex(ownerAddress)) + }); + Data initializeFuncEncoded; + initializeFunc.encode(initializeFuncEncoded); + return initializeFuncEncoded; +} + +std::string getEIP4337DeploymentAddress(const std::string& factoryAddress, const std::string& logicAddress, const std::string& ownerAddress) { + const Data logicInitializeBytecode = getEIP4337LogicInitializeBytecode(ownerAddress); + const Data proxyInitCode = getEIP1967ProxyInitCode(logicAddress, logicInitializeBytecode); + const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); + const Data initCodeHash = Hash::keccak256(proxyInitCode); + return create2AddressString(factoryAddress, salt, initCodeHash); +} + +} // namespace TW::Ethereum diff --git a/src/Ethereum/EIP4337.h b/src/Ethereum/EIP4337.h new file mode 100644 index 00000000000..540bd2dd6e4 --- /dev/null +++ b/src/Ethereum/EIP4337.h @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 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" + +namespace TW::Ethereum { + +std::string getEIP4337DeploymentAddress(const std::string& factoryAddress, const std::string& logicAddress, const std::string& ownerAddress); +Data getEIP4337LogicInitializeBytecode(const std::string& ownerAddress); + +} diff --git a/src/interface/TWEthereum.cpp b/src/interface/TWEthereum.cpp index e08c0f12c79..ee8c42a7b85 100644 --- a/src/interface/TWEthereum.cpp +++ b/src/interface/TWEthereum.cpp @@ -6,6 +6,7 @@ #include "Data.h" #include "Ethereum/EIP1014.h" +#include "Ethereum/EIP4337.h" #include "Ethereum/EIP2645.h" #include @@ -25,3 +26,10 @@ TWString* TWEthereumEip2645GetPath(TWString* ethAddress, TWString* layer, TWStri const auto& indexStr = *reinterpret_cast(index); return new std::string(TW::Ethereum::accountPathFromAddress(ethAddressStr, layerStr, applicationStr, indexStr)); } + +TWString* TWEthereumEip4337GetDeploymentAddress(TWString* _Nonnull factoryAddress, TWString* _Nonnull logicAddress, TWString* _Nonnull ownerAddress) { + const auto& factoryAddressStr = *reinterpret_cast(factoryAddress); + const auto& logicAddressStr = *reinterpret_cast(logicAddress); + const auto& ownerAddressStr = *reinterpret_cast(ownerAddress); + return new std::string(TW::Ethereum::getEIP4337DeploymentAddress(factoryAddressStr, logicAddressStr, ownerAddressStr)); +} \ No newline at end of file diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index 1409816761f..766e5d310ff 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -16,6 +16,14 @@ class EthereumTests: XCTestCase { let result = Ethereum.eip1014AddressCreate2(fromEthAddress: address, salt: salt, initCodeHash: initCodeHash) XCTAssertEqual(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38") } + + func testEIP4337DeploymentAddress() { + let factoryAddress = "0xd9145CCE52D386f254917e481eB44e9943F39138" + let logicAddress = "0x5C9eb5D6a6C2c1B3EFc52255C0b356f116f6f66D" + let ownerAddress = "0xA5a1dddEF094095AfB7b6e322dE72961DF2e1988" + let result = Ethereum.eip4337GetDeploymentAddress(factoryAddress: factoryAddress, logicAddress: logicAddress, ownerAddress: ownerAddress) + XCTAssertEqual(result, "0xbEaA87cEEaC906C21aaacd258FbFB87CfA3c90a8") + } func testAddress() { let anyAddress = AnyAddress(string: "0x7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1", coin: .ethereum) diff --git a/tests/chains/Ethereum/EIP1967Tests.cpp b/tests/chains/Ethereum/EIP1967Tests.cpp new file mode 100644 index 00000000000..58a7313dfb1 --- /dev/null +++ b/tests/chains/Ethereum/EIP1967Tests.cpp @@ -0,0 +1,28 @@ +// Copyright © 2017-2023 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 "TestUtilities.h" +#include +#include +#include +#include +#include + +#include + +namespace TW::Ethereum::tests { + +TEST(EthereumEip1967, Example0) { + // C++ + { + const std::string& login_address = "0x5C9eb5D6a6C2c1B3EFc52255C0b356f116f6f66D"; + const Data data = parse_hex("0xc4d66de8000000000000000000000000a5a1dddef094095afb7b6e322de72961df2e1988"); + const Data initCode = Ethereum::getEIP1967ProxyInitCode(login_address, data); + ASSERT_EQ(hexEncoded(initCode), "0x608060405260405162000c5138038062000c51833981810160405281019062000029919062000580565b6200003d828260006200004560201b60201c565b5050620007d7565b62000056836200008860201b60201c565b600082511180620000645750805b156200008357620000818383620000df60201b620000371760201c565b505b505050565b62000099816200011560201b60201c565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b60606200010d838360405180606001604052806027815260200162000c2a60279139620001eb60201b60201c565b905092915050565b6200012b816200027d60201b620000641760201c565b6200016d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000164906200066d565b60405180910390fd5b80620001a77f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b620002a060201b620000871760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60606000808573ffffffffffffffffffffffffffffffffffffffff1685604051620002179190620006dc565b600060405180830381855af49150503d806000811462000254576040519150601f19603f3d011682016040523d82523d6000602084013e62000259565b606091505b50915091506200027286838387620002aa60201b60201c565b925050509392505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b606083156200031a5760008351036200031157620002ce856200027d60201b60201c565b62000310576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620003079062000745565b60405180910390fd5b5b8290506200032d565b6200032c83836200033560201b60201c565b5b949350505050565b600082511115620003495781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200037f9190620007b3565b60405180910390fd5b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620003c9826200039c565b9050919050565b620003db81620003bc565b8114620003e757600080fd5b50565b600081519050620003fb81620003d0565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000456826200040b565b810181811067ffffffffffffffff821117156200047857620004776200041c565b5b80604052505050565b60006200048d62000388565b90506200049b82826200044b565b919050565b600067ffffffffffffffff821115620004be57620004bd6200041c565b5b620004c9826200040b565b9050602081019050919050565b60005b83811015620004f6578082015181840152602081019050620004d9565b60008484015250505050565b6000620005196200051384620004a0565b62000481565b90508281526020810184848401111562000538576200053762000406565b5b62000545848285620004d6565b509392505050565b600082601f83011262000565576200056462000401565b5b81516200057784826020860162000502565b91505092915050565b600080604083850312156200059a576200059962000392565b5b6000620005aa85828601620003ea565b925050602083015167ffffffffffffffff811115620005ce57620005cd62000397565b5b620005dc858286016200054d565b9150509250929050565b600082825260208201905092915050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b600062000655602d83620005e6565b91506200066282620005f7565b604082019050919050565b60006020820190508181036000830152620006888162000646565b9050919050565b600081519050919050565b600081905092915050565b6000620006b2826200068f565b620006be81856200069a565b9350620006d0818560208601620004d6565b80840191505092915050565b6000620006ea8284620006a5565b915081905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006200072d601d83620005e6565b91506200073a82620006f5565b602082019050919050565b6000602082019050818103600083015262000760816200071e565b9050919050565b600081519050919050565b60006200077f8262000767565b6200078b8185620005e6565b93506200079d818560208601620004d6565b620007a8816200040b565b840191505092915050565b60006020820190508181036000830152620007cf818462000772565b905092915050565b61044380620007e76000396000f3fe6080604052366100135761001161001d565b005b61001b61001d565b005b610025610091565b610035610030610093565b6100a2565b565b606061005c83836040518060600160405280602781526020016103e7602791396100c8565b905092915050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b565b600061009d61014e565b905090565b3660008037600080366000845af43d6000803e80600081146100c3573d6000f35b3d6000fd5b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516100f291906102db565b600060405180830381855af49150503d806000811461012d576040519150601f19603f3d011682016040523d82523d6000602084013e610132565b606091505b5091509150610143868383876101a5565b925050509392505050565b600061017c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b610087565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606083156102075760008351036101ff576101bf85610064565b6101fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f59061034f565b60405180910390fd5b5b829050610212565b610211838361021a565b5b949350505050565b60008251111561022d5781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161026191906103c4565b60405180910390fd5b600081519050919050565b600081905092915050565b60005b8381101561029e578082015181840152602081019050610283565b60008484015250505050565b60006102b58261026a565b6102bf8185610275565b93506102cf818560208601610280565b80840191505092915050565b60006102e782846102aa565b915081905092915050565b600082825260208201905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b6000610339601d836102f2565b915061034482610303565b602082019050919050565b600060208201905081810360008301526103688161032c565b9050919050565b600081519050919050565b6000601f19601f8301169050919050565b60006103968261036f565b6103a081856102f2565b93506103b0818560208601610280565b6103b98161037a565b840191505092915050565b600060208201905081810360008301526103de818461038b565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e57dd3eafc9985be746025b6d82d4f011b9a7bb3db56f9a1eb7eadfddd376b6064736f6c63430008110033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000005c9eb5d6a6c2c1b3efc52255c0b356f116f6f66d00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000a5a1dddef094095afb7b6e322de72961df2e198800000000000000000000000000000000000000000000000000000000"); + } +} + +} diff --git a/tests/chains/Ethereum/EIP4337Tests.cpp b/tests/chains/Ethereum/EIP4337Tests.cpp new file mode 100644 index 00000000000..294a3dc75e5 --- /dev/null +++ b/tests/chains/Ethereum/EIP4337Tests.cpp @@ -0,0 +1,47 @@ +// Copyright © 2017-2023 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 "TestUtilities.h" +#include +#include +#include +#include +#include + +#include + +namespace TW::Ethereum::tests { + +TEST(EthereumEip4337, GetEIP4337LogicInitializeBytecode) { + { + const std::string& ownerAddress = "0xA5a1dddEF094095AfB7b6e322dE72961DF2e1988"; + const auto& initializeEncoded = Ethereum::getEIP4337LogicInitializeBytecode(ownerAddress); + ASSERT_EQ(hexEncoded(initializeEncoded), "0xc4d66de8000000000000000000000000a5a1dddef094095afb7b6e322de72961df2e1988"); + } +} + +// https://goerli.etherscan.io/address/0x5a87209b755781cf65feeedd3855ade0317f4a92#readContract +TEST(EthereumEip4337, GetEIP4337DeploymentAddress) { + // C++ + { + const std::string& factoryAddress = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92"; + const std::string& logicAddress = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620"; + const std::string& ownerAddress = "0x78d9C32b96Bb872D66D51818227563f44e67E238"; + const std::string& deploymentAddress = Ethereum::getEIP4337DeploymentAddress(factoryAddress, logicAddress, ownerAddress); + ASSERT_EQ(deploymentAddress, "0x8cE23B8769ac01d0df0d5f47Be1A38FeA97F3879"); + } + + // C + { + const auto factoryAddress = STRING("0x5A87209b755781cF65fEeEdd3855ade0317f4a92"); + const auto logicAddress = STRING("0x21cc27d7db4fa19857a3702653a7a67ee30ca620"); + const auto ownerAddress = STRING("0x78d9C32b96Bb872D66D51818227563f44e67E238"); + const auto& result = WRAPS(TWEthereumEip4337GetDeploymentAddress(factoryAddress.get(), logicAddress.get(), ownerAddress.get())); + assertStringsEqual(result, "0x8cE23B8769ac01d0df0d5f47Be1A38FeA97F3879"); + } +} + +} From af8b9784d83935cc93eb4ad4fcdc5f92edbf206d Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 24 Jan 2023 10:36:41 +0100 Subject: [PATCH 083/426] [Eip155-Eip712]: add eip-155 + eip712 support to sign message (#2877) --- .../ethereum/TestEthereumMessageSigner.kt | 100 ++++++- .../core/app/utils/TestHDWallet.kt | 4 +- .../TrustWalletCore/TWEthereumMessageSigner.h | 34 +++ src/Ethereum/EIP191.cpp | 38 --- src/Ethereum/MessageSigner.cpp | 75 ++++++ src/Ethereum/{EIP191.h => MessageSigner.h} | 21 +- src/interface/TWEthereumMessageSigner.cpp | 39 ++- swift/Tests/Blockchains/EthereumTests.swift | 92 ++++++- swift/Tests/HDWalletTests.swift | 4 +- tests/chains/Ethereum/EIP1014Tests.cpp | 20 +- tests/chains/Ethereum/EIP191Tests.cpp | 37 --- .../Ethereum/EthereumMessageSignerTests.cpp | 246 ++++++++++++++++++ tests/chains/Ethereum/SignerTests.cpp | 2 +- tests/common/HDWallet/HDWalletTests.cpp | 12 +- tests/interface/TWHDWalletTests.cpp | 4 +- 15 files changed, 623 insertions(+), 105 deletions(-) delete mode 100644 src/Ethereum/EIP191.cpp create mode 100644 src/Ethereum/MessageSigner.cpp rename src/Ethereum/{EIP191.h => MessageSigner.h} (60%) delete mode 100644 tests/chains/Ethereum/EIP191Tests.cpp create mode 100644 tests/chains/Ethereum/EthereumMessageSignerTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt index 0f0aae9efc4..6b86f698769 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt @@ -15,13 +15,109 @@ class TestEthereumMessageSigner { } @Test - fun testEthereumSignAndVerifyMessage() { + fun testEthereumSignAndVerifyMessageImmutableX() { val data = Numeric.hexStringToByteArray("3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84") val privateKey = PrivateKey(data) val publicKey = privateKey.getPublicKey(CoinType.ETHEREUM) val msg = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3" - val signature = EthereumMessageSigner.signMessage(privateKey, msg) + val signature = EthereumMessageSigner.signMessageImmutableX(privateKey, msg) assertEquals(signature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); assertTrue(EthereumMessageSigner.verifyMessage(publicKey, msg, signature)) } + + @Test + fun testEthereumSignAndVerifyMessageLegacy() { + val data = Numeric.hexStringToByteArray("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d") + val privateKey = PrivateKey(data) + val publicKey = privateKey.getPublicKey(CoinType.ETHEREUM) + val msg = "Foo" + val signature = EthereumMessageSigner.signMessage(privateKey, msg) + assertEquals(signature, "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be71101b"); + assertTrue(EthereumMessageSigner.verifyMessage(publicKey, msg, signature)) + } + + @Test + fun testEthereumSignAndVerifyMessage712Legacy() { + val data = Numeric.hexStringToByteArray("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d") + val privateKey = PrivateKey(data) + val publicKey = privateKey.getPublicKey(CoinType.ETHEREUM) + val msg = """ + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + } + """.trimIndent() + val signature = EthereumMessageSigner.signTypedMessage(privateKey, msg) + assertEquals(signature, "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf3761c"); + assertTrue(EthereumMessageSigner.verifyMessage(publicKey, msg, signature)) + } + + @Test + fun testEthereumSignAndVerifyMessage712Eip155() { + val data = Numeric.hexStringToByteArray("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d") + val privateKey = PrivateKey(data) + val publicKey = privateKey.getPublicKey(CoinType.ETHEREUM) + val msg = """ + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + } + """.trimIndent() + val signature = EthereumMessageSigner.signTypedMessageEip155(privateKey, msg, 0) + assertEquals(signature, "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf37624"); + assertTrue(EthereumMessageSigner.verifyMessage(publicKey, msg, signature)) + } + + @Test + fun testEthereumSignAndVerifyMessageEip155() { + val data = Numeric.hexStringToByteArray("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d") + val privateKey = PrivateKey(data) + val publicKey = privateKey.getPublicKey(CoinType.ETHEREUM) + val msg = "Foo" + val signature = EthereumMessageSigner.signMessageEip155(privateKey, msg, 0) + assertEquals(signature, "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be711023"); + assertTrue(EthereumMessageSigner.verifyMessage(publicKey, msg, signature)) + } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt index bd0faaef521..45806099afb 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt @@ -41,7 +41,7 @@ class TestHDWallet { // Retrieve Stark Private key part val ethMsg = "Only sign this request if you’ve initiated an action with Immutable X." - val ethSignature = EthereumMessageSigner.signMessage(ethPrivateKey, ethMsg) + val ethSignature = EthereumMessageSigner.signMessageImmutableX(ethPrivateKey, ethMsg) assertEquals(ethSignature, "18b1be8b78807d3326e28bc286d7ee3d068dcd90b1949ce1d25c1f99825f26e70992c5eb7f44f76b202aceded00d74f771ed751f2fe538eec01e338164914fe001") val starkPrivateKey = StarkWare.getStarkKeyFromSignature(starkDerivationPath, ethSignature) val starkPublicKey = starkPrivateKey.getPublicKeyByType(PublicKeyType.STARKEX) @@ -50,7 +50,7 @@ class TestHDWallet { // Account register val ethMsgToRegister = "Only sign this key linking request from Immutable X" - val ethSignatureToRegister = EthereumMessageSigner.signMessage(ethPrivateKey, ethMsgToRegister) + val ethSignatureToRegister = EthereumMessageSigner.signMessageImmutableX(ethPrivateKey, ethMsgToRegister) assertEquals(ethSignatureToRegister, "646da4160f7fc9205e6f502fb7691a0bf63ecbb74bbb653465cd62388dd9f56325ab1e4a9aba99b1661e3e6251b42822855a71e60017b310b9f90e990a12e1dc01") val starkMsg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf" val starkSignature = StarkExMessageSigner.signMessage(starkPrivateKey, starkMsg) diff --git a/include/TrustWalletCore/TWEthereumMessageSigner.h b/include/TrustWalletCore/TWEthereumMessageSigner.h index e33a3e3dad1..5a1db56a516 100644 --- a/include/TrustWalletCore/TWEthereumMessageSigner.h +++ b/include/TrustWalletCore/TWEthereumMessageSigner.h @@ -21,6 +21,23 @@ TW_EXTERN_C_BEGIN TW_EXPORT_STRUCT struct TWEthereumMessageSigner; +/// Sign a typed message EIP-712 V4. +/// +/// \param privateKey: the private key used for signing +/// \param messageJson: A custom typed data message in json +/// \returns the signature, Hex-encoded. On invalid input empty string is returned. Returned object needs to be deleted after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWEthereumMessageSignerSignTypedMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull messageJson); + +/// Sign a typed message EIP-712 V4 with EIP-155 replay attack protection. +/// +/// \param privateKey: the private key used for signing +/// \param messageJson: A custom typed data message in json +/// \param chainId: chainId for eip-155 protection +/// \returns the signature, Hex-encoded. On invalid input empty string is returned or invalid chainId error message. Returned object needs to be deleted after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWEthereumMessageSignerSignTypedMessageEip155(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull messageJson, int chainId); + /// Sign a message. /// /// \param privateKey: the private key used for signing @@ -29,6 +46,23 @@ struct TWEthereumMessageSigner; TW_EXPORT_STATIC_METHOD TWString* _Nonnull TWEthereumMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message); +/// Sign a message with Immutable X msg type. +/// +/// \param privateKey: the private key used for signing +/// \param message: A custom message which is input to the signing. +/// \returns the signature, Hex-encoded. On invalid input empty string is returned. Returned object needs to be deleted after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWEthereumMessageSignerSignMessageImmutableX(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message); + +/// Sign a message with Eip-155 msg type. +/// +/// \param privateKey: the private key used for signing +/// \param message: A custom message which is input to the signing. +/// \param chainId: chainId for eip-155 protection +/// \returns the signature, Hex-encoded. On invalid input empty string is returned. Returned object needs to be deleted after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWEthereumMessageSignerSignMessageEip155(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message, int chainId); + /// Verify signature for a message. /// /// \param pubKey: pubKey that will verify and recover the message from the signature diff --git a/src/Ethereum/EIP191.cpp b/src/Ethereum/EIP191.cpp deleted file mode 100644 index 309c61c959c..00000000000 --- a/src/Ethereum/EIP191.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright © 2017-2022 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 "EIP191.h" -#include "HexCoding.h" -#include -#include - -namespace TW::Ethereum::internal { - -Data generateMessage(const std::string& message) { - std::string prefix(1, MessageSigner::EthereumPrefix); - std::stringstream ss; - ss << prefix << MessageSigner::MessagePrefix << std::to_string(message.size()) << message; - Data signableMessage = Hash::keccak256(data(ss.str())); - return signableMessage; -} - -} // namespace TW::Ethereum::internal - -namespace TW::Ethereum { - -std::string MessageSigner::signMessage(const PrivateKey& privateKey, const std::string& message) { - auto signableMessage = internal::generateMessage(message); - return hex(privateKey.sign(signableMessage, TWCurveSECP256k1)); -} - -bool MessageSigner::verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept { - auto msg = internal::generateMessage(message); - auto rawSignature = parse_hex(signature); - auto recovered = publicKey.recover(rawSignature, msg); - return recovered == publicKey && publicKey.verify(rawSignature, msg); -} - -} // namespace TW::Ethereum diff --git a/src/Ethereum/MessageSigner.cpp b/src/Ethereum/MessageSigner.cpp new file mode 100644 index 00000000000..fa86a3097f2 --- /dev/null +++ b/src/Ethereum/MessageSigner.cpp @@ -0,0 +1,75 @@ +// Copyright © 2017-2022 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 "HexCoding.h" +#include "MessageSigner.h" +#include +#include +#include + +namespace TW::Ethereum::internal { + +Data generateMessage(const std::string& message) { + std::string prefix(1, MessageSigner::EthereumPrefix); + std::stringstream ss; + ss << prefix << MessageSigner::MessagePrefix << std::to_string(message.size()) << message; + Data signableMessage = Hash::keccak256(data(ss.str())); + return signableMessage; +} + +std::string commonSign(const PrivateKey& privateKey, const Data& signableMessage, MessageType msgType, TW::Ethereum::MessageSigner::MaybeChainId chainId) { + auto data = privateKey.sign(signableMessage, TWCurveSECP256k1); + switch (msgType) { + case MessageType::ImmutableX: + break; + case MessageType::Legacy: + data[64] += 27; + break; + case MessageType::Eip155: + auto id = chainId.value_or(0); + data[64] += 35 + id * 2; + break; + } + return hex(data); +} + +} // namespace TW::Ethereum::internal + +namespace TW::Ethereum { + +std::string MessageSigner::signMessage(const PrivateKey& privateKey, const std::string& message, MessageType msgType, MaybeChainId chainId) { + auto signableMessage = internal::generateMessage(message); + return internal::commonSign(privateKey, signableMessage, msgType, chainId); +} + +std::string MessageSigner::signTypedData(const PrivateKey& privateKey, const std::string& data, MessageType msgType, MessageSigner::MaybeChainId chainId) { + if (msgType == MessageType::Eip155 && nlohmann::json::accept(data)) { + auto json = nlohmann::json::parse(data); + if (json.contains("types") && json.at("types").contains("EIP712Domain")) { + if (json.at("domain").at("chainId").get() != *chainId) { + return "EIP712 chainId is different than the current chainID."; + } + } + } + auto signableMessage = ABI::ParamStruct::hashStructJson(data); + return internal::commonSign(privateKey, signableMessage, msgType, chainId); +} + +bool MessageSigner::verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept { + Data msg = internal::generateMessage(message); + //! If it's json && EIP712Domain then we hash the struct + if (nlohmann::json::accept(message)) { + auto json = nlohmann::json::parse(message); + if (json.contains("types") && json.at("types").contains("EIP712Domain")) { + msg = ABI::ParamStruct::hashStructJson(message); + } + } + auto rawSignature = parse_hex(signature); + auto recovered = publicKey.recover(rawSignature, msg); + return recovered == publicKey && publicKey.verify(rawSignature, msg); +} + +} // namespace TW::Ethereum diff --git a/src/Ethereum/EIP191.h b/src/Ethereum/MessageSigner.h similarity index 60% rename from src/Ethereum/EIP191.h rename to src/Ethereum/MessageSigner.h index c1267ab0452..4cb3c1e9598 100644 --- a/src/Ethereum/EIP191.h +++ b/src/Ethereum/MessageSigner.h @@ -8,16 +8,35 @@ #include #include +#include namespace TW::Ethereum { +enum class MessageType { + Legacy = 1, + Eip155 = 2, + ImmutableX = 3 +}; + class MessageSigner { public: + using MaybeChainId = std::optional; /// Sign a message following EIP-191 /// \param privateKey the private key to sign with /// \param message message to sign + /// \param msgType message type to sign + /// \param chainId optional chainId if msgType is eip155 + /// \return hex signed message + static std::string signMessage(const PrivateKey& privateKey, const std::string& message, MessageType msgType, MaybeChainId chainId = std::nullopt); + + /// Sign typed data according to EIP-712 V4 + /// \param privateKey the private key to sign with + /// \param data json data + /// \param msgType message type to sign + /// \param chainId optional chainId if msgType is eip155 /// \return hex signed message - static std::string signMessage(const PrivateKey& privateKey, const std::string& message); + static std::string signTypedData(const PrivateKey& privateKey, const std::string& data, MessageType msgType, MaybeChainId chainId = std::nullopt); + /// Verify a message following EIP-191 /// \param publicKey publickey to verify the signed message /// \param message message to be verified as a string diff --git a/src/interface/TWEthereumMessageSigner.cpp b/src/interface/TWEthereumMessageSigner.cpp index 19e1d6e3705..75ce9f884e4 100644 --- a/src/interface/TWEthereumMessageSigner.cpp +++ b/src/interface/TWEthereumMessageSigner.cpp @@ -6,17 +6,50 @@ #include -#include "Ethereum/EIP191.h" +#include "Ethereum/MessageSigner.h" -TWString* _Nonnull TWEthereumMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message) { +namespace TW::internal { + +TWString* _Nonnull TWEthereumMessageSignerSignCommon(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message, Ethereum::MessageType msgType, Ethereum::MessageSigner::MaybeChainId chainId = std::nullopt) { try { - const auto signature = TW::Ethereum::MessageSigner::signMessage(privateKey->impl, TWStringUTF8Bytes(message)); + const auto signature = TW::Ethereum::MessageSigner::signMessage(privateKey->impl, TWStringUTF8Bytes(message), msgType, chainId); return TWStringCreateWithUTF8Bytes(signature.c_str()); } catch (...) { return TWStringCreateWithUTF8Bytes(""); } } +TWString* _Nonnull TWEthereumMessageSignerSignTypedCommon(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message, Ethereum::MessageType msgType, Ethereum::MessageSigner::MaybeChainId chainId = std::nullopt) { + try { + const auto signature = TW::Ethereum::MessageSigner::signTypedData(privateKey->impl, TWStringUTF8Bytes(message), msgType, chainId); + return TWStringCreateWithUTF8Bytes(signature.c_str()); + } catch (...) { + return TWStringCreateWithUTF8Bytes(""); + } +} + +} // namespace TW::internal + +TWString* _Nonnull TWEthereumMessageSignerSignTypedMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull messageJson) { + return TW::internal::TWEthereumMessageSignerSignTypedCommon(privateKey, messageJson, TW::Ethereum::MessageType::Legacy); +} + +TWString* _Nonnull TWEthereumMessageSignerSignTypedMessageEip155(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull messageJson, int chainId) { + return TW::internal::TWEthereumMessageSignerSignTypedCommon(privateKey, messageJson, TW::Ethereum::MessageType::Eip155, static_cast(chainId)); +} + +TWString* _Nonnull TWEthereumMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message) { + return TW::internal::TWEthereumMessageSignerSignCommon(privateKey, message, TW::Ethereum::MessageType::Legacy); +} + +TWString* _Nonnull TWEthereumMessageSignerSignMessageImmutableX(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message) { + return TW::internal::TWEthereumMessageSignerSignCommon(privateKey, message, TW::Ethereum::MessageType::ImmutableX); +} + +TWString* _Nonnull TWEthereumMessageSignerSignMessageEip155(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message, int chainId) { + return TW::internal::TWEthereumMessageSignerSignCommon(privateKey, message, TW::Ethereum::MessageType::Eip155, static_cast(chainId)); +} + bool TWEthereumMessageSignerVerifyMessage(const struct TWPublicKey* _Nonnull publicKey, TWString* _Nonnull message, TWString* _Nonnull signature) { return TW::Ethereum::MessageSigner::verifyMessage(publicKey->impl, TWStringUTF8Bytes(message), TWStringUTF8Bytes(signature)); } diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index 766e5d310ff..4a94e459445 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -291,12 +291,100 @@ class EthereumTests: XCTestCase { XCTAssertEqual(btcAddress, "bc1q97jc0jdgsyvvhxydxxd6np8sa920c39l3qpscf") } - func testMessageAndVerifySigner() { + func testMessageAndVerifySignerImmutableX() { let privateKey = PrivateKey(data: Data(hexString: "3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84")!)! let msg = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3" - let signature = EthereumMessageSigner.signMessage(privateKey: privateKey, message: msg) + let signature = EthereumMessageSigner.signMessageImmutableX(privateKey: privateKey, message: msg) XCTAssertEqual(signature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01") let pubKey = privateKey.getPublicKey(coinType: .ethereum) XCTAssertTrue(EthereumMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) } + + func testMessageAndVerifySignerLegacy() { + let privateKey = PrivateKey(data: Data(hexString: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")!)! + let msg = "Foo" + let signature = EthereumMessageSigner.signMessage(privateKey: privateKey, message: msg) + XCTAssertEqual(signature, "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be71101b") + let pubKey = privateKey.getPublicKey(coinType: .ethereum) + XCTAssertTrue(EthereumMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) + } + + func testMessageAndVerifySignerEip155() { + let privateKey = PrivateKey(data: Data(hexString: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")!)! + let msg = "Foo" + let signature = EthereumMessageSigner.signMessageEip155(privateKey: privateKey, message: msg, chainId: 0) + XCTAssertEqual(signature, "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be711023") + let pubKey = privateKey.getPublicKey(coinType: .ethereum) + XCTAssertTrue(EthereumMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) + } + + func testMessageAndVerifySigner712Legacy() { + let privateKey = PrivateKey(data: Data(hexString: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")!)! + let msg = """ + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + } + """ + let signature = EthereumMessageSigner.signTypedMessage(privateKey: privateKey, messageJson: msg) + XCTAssertEqual(signature, "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf3761c") + let pubKey = privateKey.getPublicKey(coinType: .ethereum) + XCTAssertTrue(EthereumMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) + } + + func testMessageAndVerifySigner712Eip155() { + let privateKey = PrivateKey(data: Data(hexString: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")!)! + let msg = """ + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + } + """ + let signature = EthereumMessageSigner.signTypedMessageEip155(privateKey: privateKey, messageJson: msg, chainId: 0) + XCTAssertEqual(signature, "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf37624") + let pubKey = privateKey.getPublicKey(coinType: .ethereum) + XCTAssertTrue(EthereumMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) + } } diff --git a/swift/Tests/HDWalletTests.swift b/swift/Tests/HDWalletTests.swift index 537f8b0a961..ea077ee52e0 100644 --- a/swift/Tests/HDWalletTests.swift +++ b/swift/Tests/HDWalletTests.swift @@ -27,7 +27,7 @@ class HDWalletTests: XCTestCase { // Retrieve Stark Private key part let ethMsg = "Only sign this request if you’ve initiated an action with Immutable X." - let ethSignature = EthereumMessageSigner.signMessage(privateKey: ethPrivateKey, message: ethMsg) + let ethSignature = EthereumMessageSigner.signMessageImmutableX(privateKey: ethPrivateKey, message: ethMsg) XCTAssertEqual(ethSignature, "18b1be8b78807d3326e28bc286d7ee3d068dcd90b1949ce1d25c1f99825f26e70992c5eb7f44f76b202aceded00d74f771ed751f2fe538eec01e338164914fe001") let starkPrivateKey = StarkWare.getStarkKeyFromSignature(derivationPath: derivationPath, signature: ethSignature) XCTAssertEqual(starkPrivateKey.data.hexString, "04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de") @@ -36,7 +36,7 @@ class HDWalletTests: XCTestCase { // Account Register let ethMsgToRegister = "Only sign this key linking request from Immutable X" - let ethSignatureToRegister = EthereumMessageSigner.signMessage(privateKey: ethPrivateKey, message: ethMsgToRegister) + let ethSignatureToRegister = EthereumMessageSigner.signMessageImmutableX(privateKey: ethPrivateKey, message: ethMsgToRegister) XCTAssertEqual(ethSignatureToRegister, "646da4160f7fc9205e6f502fb7691a0bf63ecbb74bbb653465cd62388dd9f56325ab1e4a9aba99b1661e3e6251b42822855a71e60017b310b9f90e990a12e1dc01") let starkMsg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf" let starkSignature = StarkExMessageSigner.signMessage(privateKey: starkPrivateKey, message: starkMsg) diff --git a/tests/chains/Ethereum/EIP1014Tests.cpp b/tests/chains/Ethereum/EIP1014Tests.cpp index 2a9e38864bd..dab96107ebc 100644 --- a/tests/chains/Ethereum/EIP1014Tests.cpp +++ b/tests/chains/Ethereum/EIP1014Tests.cpp @@ -60,19 +60,21 @@ namespace TW::Ethereum::tests { TEST(EthereumEip1014, Example2) { const std::string& from = "0xdeadbeef00000000000000000000000000000000"; const Data salt = parse_hex("0x000000000000000000000000feed000000000000000000000000000000000000"); - Data initCodeHash = Hash::keccak256(parse_hex("0x00")); - const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); - ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0xD04116cDd17beBE565EB2422F2497E06cC1C9833"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0xD04116cDd17beBE565EB2422F2497E06cC1C9833"); + Data initCode = parse_hex("0x00"); + initCode.resize(32); + const auto& addressData = Ethereum::create2Address(from, salt, initCode); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x2DB27D1d6BE32C9abfA484BA3d591101881D4B9f"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCode), "0x2DB27D1d6BE32C9abfA484BA3d591101881D4B9f"); } TEST(EthereumEip1014, Example3) { const std::string& from = "0x0000000000000000000000000000000000000000"; const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); - Data initCodeHash = Hash::keccak256(parse_hex("0xdeadbeef")); - const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); - ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e"); + Data initCode = parse_hex("0xdeadbeef"); + initCode.resize(32); + const auto& addressData = Ethereum::create2Address(from, salt, initCode); + ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x219438aC82230Cb9A9C13Cd99D324fA1d66CF018"); + ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCode), "0x219438aC82230Cb9A9C13Cd99D324fA1d66CF018"); } TEST(EthereumEip1014, Example4) { @@ -101,7 +103,7 @@ namespace TW::Ethereum::tests { ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0"); ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0"); } - + TEST(EthereumEip1014, Example7) { const std::string& from = "0x7EF2e0048f5bAeDe046f6BF797943daF4ED8CB47"; const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); diff --git a/tests/chains/Ethereum/EIP191Tests.cpp b/tests/chains/Ethereum/EIP191Tests.cpp deleted file mode 100644 index 38c937479d1..00000000000 --- a/tests/chains/Ethereum/EIP191Tests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2022 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 -#include -#include -#include -#include -#include "TestUtilities.h" - -#include - -namespace TW::Ethereum { - TEST(EthereumEip191, signMessageAndVerify) { - PrivateKey ethKey(parse_hex("3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84")); - auto msg = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3"; - auto signature = Ethereum::MessageSigner::signMessage(ethKey, msg); - ASSERT_EQ(signature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); - auto pubKey = ethKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); - ASSERT_TRUE(Ethereum::MessageSigner::verifyMessage(pubKey, msg, signature)); - } - - TEST(TWEthereumMessageSigner, SignAndVerify) { - const auto privKeyData = "3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84"; - const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); - const auto message = STRING("Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3"); - - const auto pubKey = TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeEthereum); - const auto signature = WRAPS(TWEthereumMessageSignerSignMessage(privateKey.get(), message.get())); - EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); - EXPECT_TRUE(TWEthereumMessageSignerVerifyMessage(pubKey, message.get(), signature.get())); - delete pubKey; - } -} diff --git a/tests/chains/Ethereum/EthereumMessageSignerTests.cpp b/tests/chains/Ethereum/EthereumMessageSignerTests.cpp new file mode 100644 index 00000000000..2c818921e86 --- /dev/null +++ b/tests/chains/Ethereum/EthereumMessageSignerTests.cpp @@ -0,0 +1,246 @@ +// Copyright © 2017-2022 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 "TestUtilities.h" +#include +#include +#include +#include +#include + +#include + +namespace TW::Ethereum { + TEST(EthereumEip712, SignMessageAndVerifyLegacy) { + PrivateKey ethKey(parse_hex("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")); + auto msg = R"( + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + })"; + auto signature = Ethereum::MessageSigner::signTypedData(ethKey, msg, MessageType::Legacy); + ASSERT_EQ(signature, "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf3761c"); + auto pubKey = ethKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + ASSERT_TRUE(Ethereum::MessageSigner::verifyMessage(pubKey, msg, signature)); + } + + TEST(EthereumEip712, SignMessageAndVerifyEip155) { + PrivateKey ethKey(parse_hex("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")); + auto msg = R"( + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + })"; + auto signature = Ethereum::MessageSigner::signTypedData(ethKey, msg, MessageType::Eip155, 0); + ASSERT_EQ(signature, "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf37624"); + auto pubKey = ethKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + ASSERT_TRUE(Ethereum::MessageSigner::verifyMessage(pubKey, msg, signature)); + } + + TEST(EthereumEip712, SignMessageAndVerifyInvalidEip155) { + PrivateKey ethKey(parse_hex("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")); + auto msg = R"( + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + })"; + auto signature = Ethereum::MessageSigner::signTypedData(ethKey, msg, MessageType::Eip155, 0); + ASSERT_EQ(signature, "EIP712 chainId is different than the current chainID."); + } + + TEST(EthereumEip191, SignMessageAndVerifyLegacy) { + PrivateKey ethKey(parse_hex("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")); + auto msg = "Foo"; + auto signature = Ethereum::MessageSigner::signMessage(ethKey, msg, MessageType::Legacy); + ASSERT_EQ(signature, "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be71101b"); + auto pubKey = ethKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + ASSERT_TRUE(Ethereum::MessageSigner::verifyMessage(pubKey, msg, signature)); + } + + TEST(EthereumEip191, SignMessageAndVerifyEip155) { + PrivateKey ethKey(parse_hex("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d")); + auto msg = "Foo"; + auto signature = Ethereum::MessageSigner::signMessage(ethKey, msg, MessageType::Eip155, 0); + ASSERT_EQ(signature, "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be711023"); + auto pubKey = ethKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + ASSERT_TRUE(Ethereum::MessageSigner::verifyMessage(pubKey, msg, signature)); + } + + TEST(EthereumEip191, SignMessageAndVerifyImmutableX) { + PrivateKey ethKey(parse_hex("3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84")); + auto msg = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3"; + auto signature = Ethereum::MessageSigner::signMessage(ethKey, msg, MessageType::ImmutableX); + ASSERT_EQ(signature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); + auto pubKey = ethKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + ASSERT_TRUE(Ethereum::MessageSigner::verifyMessage(pubKey, msg, signature)); + } + + TEST(TWEthereumMessageSigner, SignAndVerifyImmutableX) { + const auto privKeyData = "3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto message = STRING("Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3"); + + const auto pubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeEthereum)); + const auto signature = WRAPS(TWEthereumMessageSignerSignMessageImmutableX(privateKey.get(), message.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); + EXPECT_TRUE(TWEthereumMessageSignerVerifyMessage(pubKey.get(), message.get(), signature.get())); + } + + TEST(TWEthereumMessageSigner, SignAndVerifyLegacy) { + const auto privKeyData = "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto message = STRING("Foo"); + + const auto pubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeEthereum)); + const auto signature = WRAPS(TWEthereumMessageSignerSignMessage(privateKey.get(), message.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be71101b"); + EXPECT_TRUE(TWEthereumMessageSignerVerifyMessage(pubKey.get(), message.get(), signature.get())); + } + + TEST(TWEthereumMessageSigner, SignAndVerifyEip155) { + const auto privKeyData = "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto message = STRING("Foo"); + + const auto pubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeEthereum)); + const auto signature = WRAPS(TWEthereumMessageSignerSignMessageEip155(privateKey.get(), message.get(), 0)); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be711023"); + EXPECT_TRUE(TWEthereumMessageSignerVerifyMessage(pubKey.get(), message.get(), signature.get())); + } + + TEST(TWEthereumEip712, SignMessageAndVerifyLegacy) { + const auto privKeyData = "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + auto msg = STRING(R"( + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + })"); + const auto pubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeEthereum)); + const auto signature = WRAPS(TWEthereumMessageSignerSignTypedMessage(privateKey.get(), msg.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf3761c"); + EXPECT_TRUE(TWEthereumMessageSignerVerifyMessage(pubKey.get(), msg.get(), signature.get())); + } + + TEST(TWEthereumEip712, SignMessageAndVerifyEip155) { + const auto privKeyData = "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + auto msg = STRING(R"( + { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"} + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "wallet", "type": "address"} + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } + })"); + const auto pubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeEthereum)); + const auto signature = WRAPS(TWEthereumMessageSignerSignTypedMessageEip155(privateKey.get(), msg.get(), 0)); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf37624"); + EXPECT_TRUE(TWEthereumMessageSignerVerifyMessage(pubKey.get(), msg.get(), signature.get())); + } +} diff --git a/tests/chains/Ethereum/SignerTests.cpp b/tests/chains/Ethereum/SignerTests.cpp index cbb9fb38e91..bb938626569 100644 --- a/tests/chains/Ethereum/SignerTests.cpp +++ b/tests/chains/Ethereum/SignerTests.cpp @@ -5,9 +5,9 @@ // file LICENSE at the root of the source code distribution tree. #include "Ethereum/Address.h" +#include "Ethereum/MessageSigner.h" #include "Ethereum/RLP.h" #include "Ethereum/Signer.h" -#include "Ethereum/EIP191.h" #include "Ethereum/Transaction.h" #include "HexCoding.h" diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp index f783dffb7e1..c5f858bbc04 100644 --- a/tests/common/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -10,9 +10,9 @@ #include "Bitcoin/SegwitAddress.h" #include "Coin.h" #include "Ethereum/Address.h" -#include "Ethereum/Signer.h" -#include "Ethereum/EIP191.h" #include "Ethereum/EIP2645.h" +#include "Ethereum/MessageSigner.h" +#include "Ethereum/Signer.h" #include "HDWallet.h" #include "Hash.h" #include "Hedera/DER.h" @@ -21,8 +21,8 @@ #include "Mnemonic.h" #include "NEAR/Address.h" #include "PublicKey.h" -#include "TestUtilities.h" #include "StarkEx/MessageSigner.h" +#include "TestUtilities.h" #include @@ -544,7 +544,7 @@ TEST(HDWallet, FromMnemonicImmutableXMainnet) { ASSERT_EQ(ethAddressFromPub, ethAddress); std::string tosign = "Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3"; - auto hexEthSignature = Ethereum::MessageSigner::signMessage(ethPrivKey, tosign); + auto hexEthSignature = Ethereum::MessageSigner::signMessage(ethPrivKey, tosign, Ethereum::MessageType::ImmutableX); ASSERT_EQ(hexEthSignature, "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01"); } @@ -577,13 +577,13 @@ TEST(HDWallet, FromMnemonicImmutableXMainnetFromSignature) { ASSERT_EQ(hex(ethPrivKey.bytes), "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"); auto ethAddressFromPub = Ethereum::Address(ethPrivKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)).string(); ASSERT_EQ(ethAddressFromPub, ethAddress); - auto signature = Ethereum::MessageSigner::signMessage(ethPrivKey, "Only sign this request if you’ve initiated an action with Immutable X."); + auto signature = Ethereum::MessageSigner::signMessage(ethPrivKey, "Only sign this request if you’ve initiated an action with Immutable X.", Ethereum::MessageType::ImmutableX); ASSERT_EQ(signature, "18b1be8b78807d3326e28bc286d7ee3d068dcd90b1949ce1d25c1f99825f26e70992c5eb7f44f76b202aceded00d74f771ed751f2fe538eec01e338164914fe001"); auto starkPrivKey = ImmutableX::getPrivateKeyFromRawSignature(parse_hex(signature), DerivationPath(derivationPath)); auto starkPubKey = starkPrivKey.getPublicKey(TWPublicKeyTypeStarkex); ASSERT_EQ(hex(starkPrivKey.bytes), "04be51a04e718c202e4dca60c2b72958252024cfc1070c090dd0f170298249de"); ASSERT_EQ(hex(starkPubKey.bytes), "00e5b9b11f8372610ef35d647a1dcaba1a4010716588d591189b27bf3c2d5095"); - auto signatureToSend = Ethereum::MessageSigner::signMessage(ethPrivKey, "Only sign this key linking request from Immutable X"); + auto signatureToSend = Ethereum::MessageSigner::signMessage(ethPrivKey, "Only sign this key linking request from Immutable X", Ethereum::MessageType::ImmutableX); ASSERT_EQ(signatureToSend, "646da4160f7fc9205e6f502fb7691a0bf63ecbb74bbb653465cd62388dd9f56325ab1e4a9aba99b1661e3e6251b42822855a71e60017b310b9f90e990a12e1dc01"); auto starkMsg = "463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf"; diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index f97644e1ad5..a73b5f44bff 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -524,7 +524,7 @@ TEST(TWHDWallet, FromMnemonicImmutableXMainnetFromSignature) { // Retrieve Stark Private key part const auto ethMsg = STRING("Only sign this request if you’ve initiated an action with Immutable X."); - const auto ethSignature = WRAPS(TWEthereumMessageSignerSignMessage(ethPrivateKey.get(), ethMsg.get())); + const auto ethSignature = WRAPS(TWEthereumMessageSignerSignMessageImmutableX(ethPrivateKey.get(), ethMsg.get())); assertStringsEqual(ethSignature, "18b1be8b78807d3326e28bc286d7ee3d068dcd90b1949ce1d25c1f99825f26e70992c5eb7f44f76b202aceded00d74f771ed751f2fe538eec01e338164914fe001"); const auto starkPrivateKey = WRAP(TWPrivateKey, TWStarkWareGetStarkKeyFromSignature(starkDerivationPath.get(), ethSignature.get())); const auto starkPrivateKeyData = WRAPD(TWPrivateKeyData(starkPrivateKey.get())); @@ -535,7 +535,7 @@ TEST(TWHDWallet, FromMnemonicImmutableXMainnetFromSignature) { // Account register const auto ethMsgToRegister = STRING("Only sign this key linking request from Immutable X"); - const auto ethSignatureToRegister = WRAPS(TWEthereumMessageSignerSignMessage(ethPrivateKey.get(), ethMsgToRegister.get())); + const auto ethSignatureToRegister = WRAPS(TWEthereumMessageSignerSignMessageImmutableX(ethPrivateKey.get(), ethMsgToRegister.get())); assertStringsEqual(ethSignatureToRegister, "646da4160f7fc9205e6f502fb7691a0bf63ecbb74bbb653465cd62388dd9f56325ab1e4a9aba99b1661e3e6251b42822855a71e60017b310b9f90e990a12e1dc01"); const auto starkMsg = STRING("463a2240432264a3aa71a5713f2a4e4c1b9e12bbb56083cd56af6d878217cf"); const auto starkSignature = WRAPS(TWStarkExMessageSignerSignMessage(starkPrivateKey.get(), starkMsg.get())); From 1709e798b68a93fb0ed311a9fc97ef21ba7eebc4 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 25 Jan 2023 16:36:11 +0100 Subject: [PATCH 084/426] feat(aptos): aptos tx fix pancake swap (#2888) --- rust/src/move_parser/mod.rs | 11 +++++++- .../chains/Aptos/TransactionPayloadTests.cpp | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/rust/src/move_parser/mod.rs b/rust/src/move_parser/mod.rs index ba788b5b042..2df6a089556 100644 --- a/rust/src/move_parser/mod.rs +++ b/rust/src/move_parser/mod.rs @@ -57,7 +57,7 @@ pub extern fn parse_function_argument_to_bcs(input: *const c_char) -> *const c_c TransactionArgument::U8(v) => hex::encode(bcs::to_bytes(&v).unwrap()), TransactionArgument::U64(v) => hex::encode(bcs::to_bytes(&v).unwrap()), TransactionArgument::U128(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::Address(v) => hex::encode(bcs::to_bytes(&v).unwrap()), + TransactionArgument::Address(v) => hex::encode(bcs::to_bytes(&bcs::to_bytes(&v).unwrap()).unwrap()), TransactionArgument::U8Vector(v) => hex::encode(bcs::to_bytes(&v).unwrap()), TransactionArgument::Bool(v) => hex::encode(bcs::to_bytes(&v).unwrap()), }; @@ -84,4 +84,13 @@ mod tests { let str = unsafe { CStr::from_ptr(parse_function_argument_to_bcs("5047445908\0".as_ptr() as *const c_char)).to_str().unwrap() }; assert_eq!(str, "94e9d92c01000000"); } + + #[test] + fn tests_function_argument_to_bcs_another() { + let str = unsafe { CStr::from_ptr(parse_function_argument_to_bcs("0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6\0".as_ptr() as *const c_char)).to_str().unwrap() }; + let decoded = hex::decode(str).unwrap(); + let v = vec![decoded]; + let t = hex::encode(bcs::to_bytes(&v).unwrap()); + assert_eq!(t, "012120c95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6"); + } } diff --git a/tests/chains/Aptos/TransactionPayloadTests.cpp b/tests/chains/Aptos/TransactionPayloadTests.cpp index ed393963a06..4e8203e07bb 100644 --- a/tests/chains/Aptos/TransactionPayloadTests.cpp +++ b/tests/chains/Aptos/TransactionPayloadTests.cpp @@ -10,6 +10,34 @@ namespace TW::Aptos::tests { +TEST(AptosTransactionPayload, PancakeSwapPayload) { + auto pancakeSwapPayload=R"( + { +"arguments": [ + "0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6" +], +"function": "0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d50::IFO::release", +"type": "entry_function_payload", +"type_arguments": [ + "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::BUSD", + "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::DAI", + "0x9936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a::uints::U1" +] +} +)"_json; + + TransactionPayload payload = EntryFunction::from_json(pancakeSwapPayload); + BCS::Serializer serializer; + Address sender("0x2ce519d8cd60e0870e874e8000e8cbc87c8172e6acdbec83662b4c8cc3fc3de9"); + std::uint64_t sequenceNumber{75}; + std::uint64_t gasAmount{488130}; + std::uint64_t gasPrice{100}; + std::uint64_t expirationTime{199940521552}; + std::uint8_t chainId{1}; + serializer << sender << sequenceNumber << payload << gasAmount << gasPrice << expirationTime << chainId; + ASSERT_EQ(hex(serializer.bytes), "2ce519d8cd60e0870e874e8000e8cbc87c8172e6acdbec83662b4c8cc3fc3de94b0000000000000002c23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d500349464f0772656c65617365030748e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced9051705636f696e730442555344000748e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced9051705636f696e730344414900079936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a0575696e747302553100012120c95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6c2720700000000006400000000000000503e628d2e00000001"); +} + TEST(AptosTransactionPayload, PayLoadBasis) { ModuleId module(Address::one(), "coin"); std::uint64_t amount{1000}; From 7772467b2f72cfe32d421c52ddd83ac853259b0f Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 27 Jan 2023 12:29:56 +0100 Subject: [PATCH 085/426] [Sui]: Add SUI Implementation (#2883) --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../app/blockchains/sui/TestSuiAddress.kt | 35 ++++++ .../core/app/blockchains/sui/TestSuiSigner.kt | 42 +++++++ docs/registry.md | 1 + include/TrustWalletCore/TWBlockchain.h | 1 + include/TrustWalletCore/TWCoinType.h | 1 + registry.json | 28 +++++ rust/Cargo.lock | 9 +- rust/Cargo.toml | 1 + rust/cbindgen.toml | 3 + rust/src/encoding/mod.rs | 115 ++++++++++++++++++ rust/src/lib.rs | 1 + src/Aptos/Address.cpp | 66 +--------- src/Aptos/Address.h | 38 ++---- src/Base64.cpp | 91 +++++--------- src/Base64.h | 2 +- src/Coin.cpp | 5 +- src/Move/Address.h | 106 ++++++++++++++++ src/Sui/Address.cpp | 24 ++++ src/Sui/Address.h | 39 ++++++ src/Sui/Entry.cpp | 26 ++++ src/Sui/Entry.h | 20 +++ src/Sui/Signer.cpp | 45 +++++++ src/Sui/Signer.h | 25 ++++ src/proto/Sui.proto | 34 ++++++ swift/Tests/Blockchains/SuiTests.swift | 40 ++++++ swift/Tests/CoinAddressDerivationTests.swift | 3 + tests/chains/Sui/AddressTests.cpp | 53 ++++++++ tests/chains/Sui/SignerTests.cpp | 65 ++++++++++ tests/chains/Sui/TWCoinTypeTests.cpp | 33 +++++ tests/common/Base64Tests.cpp | 9 ++ tests/common/CoinAddressDerivationTests.cpp | 3 + tests/common/HDWallet/HDWalletTests.cpp | 13 ++ .../common/rust/bindgen/WalletCoreRsTests.cpp | 1 - tools/install-wasm-dependencies | 2 +- walletconsole/lib/Util.cpp | 9 +- wasm/CMakeLists.txt | 2 +- wasm/tests/Blockchain/Sui.test.ts | 28 +++++ 38 files changed, 858 insertions(+), 162 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt create mode 100644 rust/cbindgen.toml create mode 100644 rust/src/encoding/mod.rs create mode 100644 src/Move/Address.h create mode 100644 src/Sui/Address.cpp create mode 100644 src/Sui/Address.h create mode 100644 src/Sui/Entry.cpp create mode 100644 src/Sui/Entry.h create mode 100644 src/Sui/Signer.cpp create mode 100644 src/Sui/Signer.h create mode 100644 src/proto/Sui.proto create mode 100644 swift/Tests/Blockchains/SuiTests.swift create mode 100644 tests/chains/Sui/AddressTests.cpp create mode 100644 tests/chains/Sui/SignerTests.cpp create mode 100644 tests/chains/Sui/TWCoinTypeTests.cpp create mode 100644 wasm/tests/Blockchain/Sui.test.ts 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 dd3b7491a5c..84ed86b221f 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 @@ -108,6 +108,7 @@ class CoinAddressDerivationTests { EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) TON -> assertEquals("EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9", address) APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) + SUI -> assertEquals("0x061ce2b2100a71bb7aa0da98998887ad82597948", address) HEDERA -> assertEquals("0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5", address) SECRET -> assertEquals("secret1f69sk5033zcdr2p2yf3xjehn7xvgdeq09d2llh", address) NATIVEINJECTIVE -> assertEquals("inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", address) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiAddress.kt new file mode 100644 index 00000000000..87ae5c400e0 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiAddress.kt @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 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.sui + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestSuiAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val any = AnyAddress("0x061ce2b2100a71bb7aa0da98998887ad82597948", CoinType.SUI) + assertEquals(any.coin(), CoinType.SUI) + assertEquals(any.description(), "0x061ce2b2100a71bb7aa0da98998887ad82597948") + + Assert.assertFalse( + AnyAddress.isValid( + "0xMQqpqMQgCBuiPkoXfgZZsJvuzCeI1zc00z6vHJj4", + CoinType.SUI + ) + ) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt new file mode 100644 index 00000000000..9a5782eebd0 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt @@ -0,0 +1,42 @@ +// Copyright © 2017-2022 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.sui + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHexBytesInByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType +import wallet.core.jni.proto.Sui + +class TestSuiSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun SuiTransactionSigning() { + // Successfully broadcasted https://explorer.sui.io/transaction/rxLgxcAqgMg8gphp6eCsSGQcdZnwFYx2SRdwEhnAUC4 + val txBytes = """ + AAUCLiNiMy/EzosKCk5EZr5QQZmMVLnvAAAAAAAAACDqj/OT+1+qyLZKV4YLw8kpK3/bTZKspTUmh1pBuUfHPLb0crwkV1LQcBARaxER8XhTNJmK7wAAAAAAAAAgaQEguOdXa+m16IM536nsveakQ4u/GYJAc1fpYGGKEvgBQUP35yxF+cEL5qm153kw18dVeuYB6AMAAAAAAAAttQCskZzd41GsNuNxHYMsbbl2aS4jYjMvxM6LCgpORGa+UEGZjFS57wAAAAAAAAAg6o/zk/tfqsi2SleGC8PJKSt/202SrKU1JodaQblHxzwBAAAAAAAAAOgDAAAAAAAA + """.trimIndent() + val key = + "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266".toHexBytesInByteString() + val signDirect = Sui.SignDirect.newBuilder().setUnsignedTxMsg(txBytes).build() + val signingInput = + Sui.SigningInput.newBuilder().setSignDirectMessage(signDirect).setPrivateKey(key).build() + val result = AnySigner.sign(signingInput, CoinType.SUI, Sui.SigningOutput.parser()) + val expectedSignature = "AIYRmHDpQesfAx3iWBCMwInf3MZ56ZQGnPWNtECFjcSq0ssAgjRW6GLnFCX24tfDNjSm9gjYgoLmn1No15iFJAtqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg==" + assertEquals(result.unsignedTx, txBytes); + assertEquals(result.signature, expectedSignature) + } +} diff --git a/docs/registry.md b/docs/registry.md index 0f0784f6e60..65da87087a0 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -55,6 +55,7 @@ This list is generated from [./registry.json](../registry.json) | 607 | TON | TON | | | | 637 | Aptos | APT | | | | 714 | BNB Beacon Chain | BNB | | | +| 784 | Sui | SUI | | | | 818 | VeChain | VET | | | | 820 | Callisto | CLO | | | | 888 | NEO | NEO | | | diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 4231010137d..c93bab5e2b9 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -57,6 +57,7 @@ enum TWBlockchain { TWBlockchainAptos = 43, // Aptos TWBlockchainHedera = 44, // Hedera TWBlockchainTheOpenNetwork = 45, + TWBlockchainSui = 46, }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 39b3c9bdeb0..22a3540c265 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -130,6 +130,7 @@ enum TWCoinType { TWCoinTypeNativeInjective = 10000060, TWCoinTypeAgoric = 564, TWCoinTypeTON = 607, + TWCoinTypeSui = 784, }; /// Returns the blockchain for a coin type. diff --git a/registry.json b/registry.json index 2bfa2e3ad3c..07081485815 100644 --- a/registry.json +++ b/registry.json @@ -429,6 +429,34 @@ "documentation": "https://fullnode.mainnet.aptoslabs.com/v1/spec#/" } }, + { + "id": "sui", + "name": "Sui", + "coinId": 784, + "symbol": "SUI", + "decimals": 9, + "blockchain": "Sui", + "derivation": [ + { + "path": "m/44'/784'/0'/0'/0'" + } + ], + "curve": "ed25519", + "publicKeyType": "ed25519", + "explorer": { + "url": "https://explorer.sui.io/", + "txPath": "/transaction/", + "accountPath": "/address/", + "sampleTx": "SWRW1RoMHxnD9NeobgBoC4cXGwp2Hc511CnfWUoTBmo", + "sampleAccount": "0x62107e1afefccc7b2267ab74e332c146f5c2ca15" + }, + "info": { + "url": "https://sui.io/", + "source": "https://github.com/MystenLabs/sui", + "rpc": "https://fullnode.testnet.sui.io", + "documentation": "https://docs.sui.io/" + } + }, { "id": "cosmos", "name": "Cosmos", diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 00747873f1b..3efb0c41ea4 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -97,6 +97,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "bcs" version = "0.1.4" @@ -806,7 +812,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95540acef038bfdf3c91da323cedf0fd335f73899152cabdf407033fc7560713" dependencies = [ - "base64", + "base64 0.13.1", "ethereum-types", "hex", "serde", @@ -994,6 +1000,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wallet-core-rs" version = "0.1.0" dependencies = [ + "base64 0.21.0", "bcs", "hex", "move-core-types", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b3a7546634b..7752a93202d 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -16,5 +16,6 @@ starknet-ff = "0.1.0" starknet-signers = "0.1.0" bcs = "0.1.4" hex = "0.4.3" +base64 = "0.21.0" [dev-dependencies] diff --git a/rust/cbindgen.toml b/rust/cbindgen.toml new file mode 100644 index 00000000000..0ccb2ad1451 --- /dev/null +++ b/rust/cbindgen.toml @@ -0,0 +1,3 @@ +# Whether to add a `#pragma once` guard +# default: doesn't emit a `#pragma once` +pragma_once = true diff --git a/rust/src/encoding/mod.rs b/rust/src/encoding/mod.rs new file mode 100644 index 00000000000..82bff00e40b --- /dev/null +++ b/rust/src/encoding/mod.rs @@ -0,0 +1,115 @@ +// Copyright © 2017-2023 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. + +use base64::{Engine as _, engine::{general_purpose}}; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +#[no_mangle] +pub extern "C" fn encode_base64(data: *const u8, len: usize, is_url: bool) -> *mut c_char { + let data = unsafe { std::slice::from_raw_parts(data, len) }; + let encoded = if is_url { + general_purpose::URL_SAFE.encode(data) + } else { + general_purpose::STANDARD.encode(data) + }; + CString::new(encoded).unwrap().into_raw() +} + +#[repr(C)] +pub struct CByteArray { + data: *mut u8, + size: usize, +} + +#[no_mangle] +pub extern "C" fn decode_base64(data: *const c_char, is_url: bool) -> CByteArray { + if data.is_null() { + return CByteArray { data: std::ptr::null_mut(), size: 0 }; + } + let c_str = unsafe { CStr::from_ptr(data) }; + let str_slice = c_str.to_str().unwrap(); + let decoded = if is_url { + general_purpose::URL_SAFE + .decode(str_slice) + } else { + general_purpose::STANDARD + .decode(str_slice) + }; + let decoded = match decoded { + Ok(decoded) => decoded, + Err(_) => return CByteArray { data: std::ptr::null_mut(), size: 0 } + }; + let size = decoded.len(); + let mut decoded_vec = decoded.to_vec(); + let ptr = decoded_vec.as_mut_ptr(); + std::mem::forget(decoded_vec); + CByteArray { data: ptr, size } +} + + +#[cfg(test)] +mod tests { + use std::ffi::CString; + use crate::encoding::{decode_base64, encode_base64}; + + #[test] + fn test_encode_base64_ffi() { + let data = b"hello world"; + let encoded = unsafe { + std::ffi::CStr::from_ptr(encode_base64(data.as_ptr(), data.len(), false)) + }; + let expected = "aGVsbG8gd29ybGQ="; + assert_eq!(encoded.to_str().unwrap(), expected); + } + + #[test] + fn test_encode_base64_url_ffi() { + let data = b"+'?ab"; + let encoded = unsafe { + std::ffi::CStr::from_ptr(encode_base64(data.as_ptr(), data.len(), true)) + }; + let expected = "Kyc_YWI="; + assert_eq!(encoded.to_str().unwrap(), expected); + } + + #[test] + fn test_decode_base64_url() { + let encoded = "Kyc_YWI="; + let expected = b"+'?ab"; + + let encoded_c_str = CString::new(encoded).unwrap(); + let encoded_ptr = encoded_c_str.as_ptr(); + + let decoded_ptr = decode_base64(encoded_ptr, true); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + + assert_eq!(decoded_slice, expected); + } + + #[test] + fn test_decode_base64() { + let encoded = "aGVsbG8gd29ybGQh"; + let expected = b"hello world!"; + + let encoded_c_str = CString::new(encoded).unwrap(); + let encoded_ptr = encoded_c_str.as_ptr(); + + let decoded_ptr = decode_base64(encoded_ptr, false); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + + assert_eq!(decoded_slice, expected); + } + + #[test] + fn test_decode_base64_invalid() { + let invalid_encoded = "_This_is_an_invalid_base64_"; + let encoded_c_str = CString::new(invalid_encoded).unwrap(); + let encoded_ptr = encoded_c_str.as_ptr(); + let decoded_ptr = decode_base64(encoded_ptr, false); + assert_eq!(decoded_ptr.data.is_null(), true); + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 01bae1bc0ae..e0efd13f8a2 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -7,3 +7,4 @@ pub mod move_parser; pub mod memory; pub mod starknet; +pub mod encoding; diff --git a/src/Aptos/Address.cpp b/src/Aptos/Address.cpp index 30c1b587872..2688e58c279 100644 --- a/src/Aptos/Address.cpp +++ b/src/Aptos/Address.cpp @@ -8,76 +8,18 @@ #include "Address.h" #include "HexCoding.h" -namespace { - -std::string normalize(const std::string& string, std::size_t hexLen) { - std::string hexStr((TW::Aptos::Address::size * 2) - hexLen, '0'); - hexStr.append(string); - return hexStr; -} - -} // namespace - namespace TW::Aptos { -bool Address::isValid(const std::string& string) { - auto address = string; - if (address.starts_with("0x")) { - address = address.substr(2); - if (std::size_t hexLen = address.size(); hexLen < Address::size * 2) { - address = normalize(address, hexLen); - } - } - if (address.size() != 2 * Address::size) { - return false; - } - const auto data = parse_hex(address); - return isValid(data); +Address::Address(const std::string& string) : Address::AptosAddress(string) { } -Address::Address(const std::string& string) { - if (!isValid(string)) { - throw std::invalid_argument("Invalid address string"); - } - auto hexFunctor = [&string]() { - if (std::size_t hexLen = string.size() - 2; string.starts_with("0x") && hexLen < Address::size * 2) { - //! We have specific address like 0x1, padding it. - return parse_hex(normalize(string.substr(2), hexLen)); - } else { - return parse_hex(string); - } - }; - - const auto data = hexFunctor(); - std::copy(data.begin(), data.end(), bytes.begin()); +Address::Address(const PublicKey& publicKey): Address::AptosAddress(publicKey) { } -Address::Address(const Data& data) { - if (!isValid(data)) { - throw std::invalid_argument("Invalid address data"); - } - std::copy(data.begin(), data.end(), bytes.begin()); -} - -Address::Address(const PublicKey& publicKey) { - if (publicKey.type != TWPublicKeyTypeED25519) { - throw std::invalid_argument("Invalid public key type"); - } +Data Address::getDigest(const PublicKey& publicKey) { auto key_data = publicKey.bytes; append(key_data, 0x00); - const auto data = Hash::sha3_256(key_data); - std::copy(data.begin(), data.end(), bytes.begin()); -} - -std::string Address::string(bool withPrefix) const { - std::string output = withPrefix ? "0x" : ""; - return output + hex(bytes); -} - -std::string Address::shortString() const { - std::string s = hex(bytes); - s.erase(0, s.find_first_not_of('0')); - return s; + return key_data; } BCS::Serializer& operator<<(BCS::Serializer& stream, Address addr) noexcept { diff --git a/src/Aptos/Address.h b/src/Aptos/Address.h index 328c387edf4..0e372a88e93 100644 --- a/src/Aptos/Address.h +++ b/src/Aptos/Address.h @@ -9,53 +9,29 @@ #include "BCS.h" #include "Data.h" -#include "../PublicKey.h" +#include "Move/Address.h" +#include "PublicKey.h" #include namespace TW::Aptos { -class Address { +class Address : public Move::Address { public: - static constexpr size_t size = 32; - - std::array bytes; - - /// Determines whether a collection of bytes makes a valid address. - static bool isValid(const Data& data) { return data.size() == size; } - - /// Determines whether a string makes a valid address. - static bool isValid(const std::string& string); - - static Address zero() { - return Address("0x0"); - } - - static Address one() { - return Address("0x1"); - } - - static Address three() { - return Address("0x3"); - } + using AptosAddress = Move::Address; + using AptosAddress::size; + using AptosAddress::bytes; /// Initializes an Aptos address with a string representation. explicit Address(const std::string& string); - /// Initializes an Aptos address with a collection of bytes - explicit Address(const Data& data); - /// Initializes an Aptos address with a public key. explicit Address(const PublicKey& publicKey); /// Constructor that allow factory programming; Address() noexcept = default; - /// Returns a string representation of the address. - [[nodiscard]] std::string string(bool withPrefix = true) const; - - /// Returns a short string representation of the address. E.G 0x1; - [[nodiscard]] std::string shortString() const; + Data getDigest(const PublicKey& publicKey); }; constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcept { diff --git a/src/Base64.cpp b/src/Base64.cpp index edb5db03998..a09f399e3c7 100644 --- a/src/Base64.cpp +++ b/src/Base64.cpp @@ -1,15 +1,35 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 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 "Base64.h" +#include "rust/bindgen/WalletCoreRSBindgen.h" -#include -#include -#include -#include +namespace TW::Base64::internal { + +std::string encode(const Data& val, bool is_url) { + char* encoded = encode_base64(val.data(), val.size(), is_url); + std::string encoded_str(encoded); + free_string(encoded); + return encoded_str; +} + +Data decode(const std::string& val, bool is_url) { + if (val.empty()) { + return Data(); + } + auto decoded = decode_base64(val.c_str(), is_url); + if (decoded.data == nullptr) { + return Data(); + } + std::vector decoded_vec(&decoded.data[0], &decoded.data[decoded.size]); + std::free(decoded.data); + return decoded_vec; +} + +} namespace TW::Base64 { @@ -36,65 +56,20 @@ bool isBase64orBase64Url(const string& val) { return isBase64Any(val, base64_chars) || isBase64Any(val, base64_url_chars); } -Data decode(const string& val) { - using namespace boost::archive::iterators; - using It = transform_width, 8, 6>; - return boost::algorithm::trim_right_copy_if(Data(It(begin(val)), It(end(val))), - [](char c) { return c == '\0'; }); -} - -string encode(const Data& val) { - using namespace boost::archive::iterators; - using It = base64_from_binary>; - auto encoded = string(It(begin(val)), It(end(val))); - return encoded.append((3 - val.size() % 3) % 3, '='); -} - -/// Convert from Base64Url format to regular -void convertFromBase64Url(string& b) { - // '-' and '_' (Base64URL format) are changed to '+' and '/' - // in-place replace - size_t n = b.length(); - char* start = b.data(); - char* end = start + n; - for (auto* p = start; p < end; ++p) { - if (*p == '-') { - *p = '+'; - } else if (*p == '_') { - *p = '/'; - } - } +Data decodeBase64Url(const string& val) { + return internal::decode(val, true); } -/// Convert from regular format to Base64Url -void convertToBase64Url(string& b) { - // '+' and '/' are changed to '-' and '_' (Base64URL format) - // in-place replace - size_t n = b.length(); - char* start = b.data(); - char* end = start + n; - for (auto* p = start; p < end; ++p) { - if (*p == '+') { - *p = '-'; - } else if (*p == '/') { - *p = '_'; - } - } +string encodeBase64Url(const Data& val) { + return internal::encode(val, true); } -Data decodeBase64Url(const string& val) { - string base64Url = val; - convertFromBase64Url(base64Url); - return decode(base64Url); +std::string encode(const Data& val) { + return internal::encode(val, false); } -string encodeBase64Url(const Data& val) { - using namespace boost::archive::iterators; - using It = base64_from_binary>; - auto encoded = string(It(begin(val)), It(end(val))); - encoded.append((3 - val.size() % 3) % 3, '='); - convertToBase64Url(encoded); - return encoded; +Data decode(const string& val) { + return internal::decode(val, false); } } // namespace TW::Base64 diff --git a/src/Base64.h b/src/Base64.h index 8ea6f74356b..eacd9e147a5 100644 --- a/src/Base64.h +++ b/src/Base64.h @@ -17,7 +17,7 @@ bool isBase64orBase64Url(const std::string& val); Data decode(const std::string& val); // Encode bytes into Base64 string -std::string encode(const Data& val); +std::string encode(const TW::Data& val); // Decode a Base64Url-format or a regular Base64 string. // Base64Url format uses '-' and '_' as the two special characters, Base64 uses '+'and '/'. diff --git a/src/Coin.cpp b/src/Coin.cpp index 521db9fb0a8..167fb0026b2 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2022 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -57,6 +57,7 @@ #include "Zilliqa/Entry.h" #include "Hedera/Entry.h" #include "TheOpenNetwork/Entry.h" +#include "Sui/Entry.h" // end_of_coin_includes_marker_do_not_modify using namespace TW; @@ -107,6 +108,7 @@ Nervos::Entry NervosDP; Everscale::Entry EverscaleDP; Hedera::Entry HederaDP; TheOpenNetwork::Entry tonDP; +Sui::Entry SuiDP; // end_of_coin_dipatcher_declarations_marker_do_not_modify CoinEntry* coinDispatcher(TWCoinType coinType) { @@ -159,6 +161,7 @@ CoinEntry* coinDispatcher(TWCoinType coinType) { case TWBlockchainAptos: entry = &AptosDP; break; case TWBlockchainHedera: entry = &HederaDP; break; case TWBlockchainTheOpenNetwork: entry = &tonDP; break; + case TWBlockchainSui: entry = &SuiDP; break; // end_of_coin_dipatcher_switch_marker_do_not_modify default: entry = nullptr; break; diff --git a/src/Move/Address.h b/src/Move/Address.h new file mode 100644 index 00000000000..216f1d209d6 --- /dev/null +++ b/src/Move/Address.h @@ -0,0 +1,106 @@ +// Copyright © 2017-2023 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 "HexCoding.h" +#include "PublicKey.h" +#include + +namespace TW::Move { +template +class Address { +private: + static std::string normalize(const std::string& string, std::size_t hexLen) { + std::string hexStr((size * 2) - hexLen, '0'); + hexStr.append(string); + return hexStr; + } + + /// Determines whether a collection of bytes makes a valid address. + static bool isValid(const Data& data) { return data.size() == size; } +public: + static constexpr int size = N; + std::array bytes; + + /// Determines whether a string makes a valid address. + static bool isValid(const std::string& string) { + auto address = string; + if (address.starts_with("0x")) { + address = address.substr(2); + if (std::size_t hexLen = address.size(); hexLen < Address::size * 2) { + address = normalize(address, hexLen); + } + } + if (address.size() != 2 * Address::size) { + return false; + } + const auto data = parse_hex(address); + return isValid(data); + }; + + Address() noexcept = default; + + Address(const std::string& string) { + if (!isValid(string)) { + throw std::invalid_argument("Invalid address string"); + } + auto hexFunctor = [&string]() { + if (std::size_t hexLen = string.size() - 2; string.starts_with("0x") && hexLen < size * 2) { + //! We have specific address like 0x1, padding it. + return parse_hex(normalize(string.substr(2), hexLen)); + } else { + return parse_hex(string); + } + }; + + const auto data = hexFunctor(); + std::copy(data.begin(), data.end(), bytes.begin()); + } + + Address(const Data& data) { + if (!isValid(data)) { + throw std::invalid_argument("Invalid address data"); + } + std::copy_n(data.begin(), size, bytes.begin()); + } + + Address(const PublicKey& publicKey) { + if (publicKey.type != TWPublicKeyTypeED25519) { + throw std::invalid_argument("Invalid public key type"); + } + auto digest = static_cast(this)->getDigest(publicKey); + const auto data = Hash::sha3_256(digest); + std::copy_n(data.begin(), Address::size, bytes.begin()); + } + + static Derived zero() { + return Derived("0x0"); + } + + static Derived one() { + return Derived("0x1"); + } + + static Derived three() { + return Derived("0x3"); + } + + /// Returns a string representation of the address. + [[nodiscard]] std::string string(bool withPrefix = true) const { + std::string output = withPrefix ? "0x" : ""; + return output + hex(bytes); + }; + + /// Returns a short string representation of the address. E.G 0x1; + [[nodiscard]] std::string shortString() const { + std::string s = hex(bytes); + s.erase(0, s.find_first_not_of('0')); + return s; + }; +}; +} // namespace TW::Move diff --git a/src/Sui/Address.cpp b/src/Sui/Address.cpp new file mode 100644 index 00000000000..63bba970c76 --- /dev/null +++ b/src/Sui/Address.cpp @@ -0,0 +1,24 @@ +// Copyright © 2017-2023 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 "HexCoding.h" + +namespace TW::Sui { + +Address::Address(const std::string& string) : Address::SuiAddress(string) { +} + +Address::Address(const PublicKey& publicKey): Address::SuiAddress(publicKey) { +} + +Data Address::getDigest(const PublicKey& publicKey) { + auto key_data = Data{0x00}; + append(key_data, publicKey.bytes); + return key_data; +} + +} // namespace TW::Sui diff --git a/src/Sui/Address.h b/src/Sui/Address.h new file mode 100644 index 00000000000..ccdf055a1c1 --- /dev/null +++ b/src/Sui/Address.h @@ -0,0 +1,39 @@ +// Copyright © 2017-2023 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 "Move/Address.h" + +#include + +namespace TW::Sui { + +class Address : public Move::Address { +public: + using SuiAddress = Move::Address; + using SuiAddress::size; + using SuiAddress::bytes; + + /// Initializes an Sui address with a string representation. + explicit Address(const std::string& string); + + /// Initializes an Sui address with a public key. + explicit Address(const PublicKey& publicKey); + + /// Constructor that allow factory programming; + Address() noexcept = default; + + Data getDigest(const PublicKey& publicKey); +}; + +constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcept { + return lhs.bytes == rhs.bytes; +} + +} // namespace TW::Sui diff --git a/src/Sui/Entry.cpp b/src/Sui/Entry.cpp new file mode 100644 index 00000000000..8df448495ca --- /dev/null +++ b/src/Sui/Entry.cpp @@ -0,0 +1,26 @@ +// Copyright © 2017-2023 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" + +namespace TW::Sui { + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return Address::isValid(address); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return Address(publicKey).string(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +} // namespace TW::Sui diff --git a/src/Sui/Entry.h b/src/Sui/Entry.h new file mode 100644 index 00000000000..75cfde95062 --- /dev/null +++ b/src/Sui/Entry.h @@ -0,0 +1,20 @@ +// Copyright © 2017-2023 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::Sui { + +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; +}; + +} // namespace TW::Sui diff --git a/src/Sui/Signer.cpp b/src/Sui/Signer.cpp new file mode 100644 index 00000000000..49e9b247f63 --- /dev/null +++ b/src/Sui/Signer.cpp @@ -0,0 +1,45 @@ +// Copyright © 2017-2023 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 "Address.h" +#include "Base64.h" +#include "PublicKey.h" + +namespace { + +enum IntentScope : int { + TransactionData = 0, +}; + +enum IntentVersion : int { + V0 = 0, +}; + +enum IntentAppId { + Sui = 0 +}; + +} // namespace + +namespace TW::Sui { + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { + auto protoOutput = Proto::SigningOutput(); + auto unsignedTx = input.sign_direct_message().unsigned_tx_msg(); + auto unsignedTxData = TW::Base64::decode(unsignedTx); + Data toSign{TransactionData, V0, IntentAppId::Sui}; + append(toSign, unsignedTxData); + auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); + Data signatureScheme{0x00}; + append(signatureScheme, privateKey.sign(toSign, TWCurveED25519)); + append(signatureScheme, privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes); + protoOutput.set_unsigned_tx(unsignedTx); + protoOutput.set_signature(TW::Base64::encode(signatureScheme)); + return protoOutput; +} + +} // namespace TW::Sui diff --git a/src/Sui/Signer.h b/src/Sui/Signer.h new file mode 100644 index 00000000000..2b158464c88 --- /dev/null +++ b/src/Sui/Signer.h @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 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 "PrivateKey.h" +#include "proto/Sui.pb.h" + +namespace TW::Sui { + +/// Helper class that performs Sui transaction signing. +class Signer { +public: + /// Hide default constructor + Signer() = delete; + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; +}; + +} // namespace TW::Sui diff --git a/src/proto/Sui.proto b/src/proto/Sui.proto new file mode 100644 index 00000000000..ae68ccf2367 --- /dev/null +++ b/src/proto/Sui.proto @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 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. + +syntax = "proto3"; + +package TW.Sui.Proto; +option java_package = "wallet.core.jni.proto"; + +// Base64 encoded msg to sign (string) +message SignDirect { + // Obtain by calling any write RpcJson on SUI + string unsigned_tx_msg = 1; +} + +// Input data necessary to create a signed transaction. +message SigningInput { + // Private key to sign the transaction (bytes) + bytes private_key = 1; + + oneof transaction_payload { + SignDirect sign_direct_message = 2; + } +} + +// Transaction signing output. +message SigningOutput { + /// The raw transaction without indent in base64 + string unsigned_tx = 1; + /// The signature encoded in base64 + string signature = 2; +} diff --git a/swift/Tests/Blockchains/SuiTests.swift b/swift/Tests/Blockchains/SuiTests.swift new file mode 100644 index 00000000000..a8a64e8892c --- /dev/null +++ b/swift/Tests/Blockchains/SuiTests.swift @@ -0,0 +1,40 @@ +// Copyright © 2017-2022 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. + +import WalletCore +import XCTest + +class SuiTests: XCTestCase { + func testAddress() { + let anyAddress = AnyAddress(string: "0x061ce2b2100a71bb7aa0da98998887ad82597948", coin: .sui) + + XCTAssertEqual(anyAddress?.description, "0x061ce2b2100a71bb7aa0da98998887ad82597948") + XCTAssertEqual(anyAddress?.coin, .sui) + + let invalid = "MQqpqMQgCBuiPkoXfgZZsJvuzCeI1zc00z6vHJj4" + XCTAssertNil(Data(hexString: invalid)) + XCTAssertNil(AnyAddress(string: invalid, coin: .sui)) + XCTAssertFalse(AnyAddress.isValid(string: invalid, coin: .sui)) + } + + func testSign() { + // Successfully broadcasted https://explorer.sui.io/transaction/rxLgxcAqgMg8gphp6eCsSGQcdZnwFYx2SRdwEhnAUC4 + let privateKeyData = Data(hexString: "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")! + let txBytes = """ +AAUCLiNiMy/EzosKCk5EZr5QQZmMVLnvAAAAAAAAACDqj/OT+1+qyLZKV4YLw8kpK3/bTZKspTUmh1pBuUfHPLb0crwkV1LQcBARaxER8XhTNJmK7wAAAAAAAAAgaQEguOdXa+m16IM536nsveakQ4u/GYJAc1fpYGGKEvgBQUP35yxF+cEL5qm153kw18dVeuYB6AMAAAAAAAAttQCskZzd41GsNuNxHYMsbbl2aS4jYjMvxM6LCgpORGa+UEGZjFS57wAAAAAAAAAg6o/zk/tfqsi2SleGC8PJKSt/202SrKU1JodaQblHxzwBAAAAAAAAAOgDAAAAAAAA +""" + let input = SuiSigningInput.with { + $0.signDirectMessage = SuiSignDirect.with { + $0.unsignedTxMsg = txBytes + } + $0.privateKey = privateKeyData + } + let output: SuiSigningOutput = AnySigner.sign(input: input, coin: .sui) + XCTAssertEqual(output.unsignedTx, txBytes) + let expectedSignature = "AIYRmHDpQesfAx3iWBCMwInf3MZ56ZQGnPWNtECFjcSq0ssAgjRW6GLnFCX24tfDNjSm9gjYgoLmn1No15iFJAtqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg==" + XCTAssertEqual(output.signature, expectedSignature) + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 1863b13d3b5..eced318a140 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -259,6 +259,9 @@ class CoinAddressDerivationTests: XCTestCase { case .aptos: let expectedResult = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .sui: + let expectedResult = "0x061ce2b2100a71bb7aa0da98998887ad82597948"; + assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .hedera: let expectedResult = "0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) diff --git a/tests/chains/Sui/AddressTests.cpp b/tests/chains/Sui/AddressTests.cpp new file mode 100644 index 00000000000..c28c9c4253e --- /dev/null +++ b/tests/chains/Sui/AddressTests.cpp @@ -0,0 +1,53 @@ +// Copyright © 2017-2023 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 "HexCoding.h" +#include "Sui/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include +#include + +namespace TW::Sui::tests { + +TEST(SuiAddress, Valid) { + ASSERT_TRUE(Address::isValid("0x1")); + // Address 20 are valid in SUI + ASSERT_TRUE(Address::isValid("0xb1dc06bd64d4e179a482b97bb68243f6c02c1b92")); + ASSERT_TRUE(Address::isValid("b1dc06bd64d4e179a482b97bb68243f6c02c1b92")); +} + +TEST(SuiAddress, Invalid) { + // Address 32 are invalid in SUI + ASSERT_FALSE(Address::isValid("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); + ASSERT_FALSE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); + ASSERT_FALSE(Address::isValid("19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c")); + // Too long + ASSERT_FALSE(Address::isValid("b1dc06bd64d4e179a482b97bb68243f6c02c1b921")); + // Too short + ASSERT_FALSE(Address::isValid("b1dc06bd64d4e179a482b97bb68243f6c02c1b9")); + // Invalid Hex + ASSERT_FALSE(Address::isValid("0xS1dc06bd64d4e179a482b97bb68243f6c02c1b92")); +} + +TEST(SuiAddress, FromString) { + auto address = Address("b1dc06bd64d4e179a482b97bb68243f6c02c1b92"); + ASSERT_EQ(address.string(), "0xb1dc06bd64d4e179a482b97bb68243f6c02c1b92"); +} + +TEST(SuiAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("088baa019f081d6eab8dff5c447f9ce2f83c1babf3d03686299eaf6a1e89156e")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(address.string(), "0xb638d15fa81d301a9756259d0c7b2ca27a00a531"); +} + +TEST(SuiAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("ad0e293a56c9fc648d1872a00521d97e6b65724519a2676c2c47cb95d131cf5a"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "0xb638d15fa81d301a9756259d0c7b2ca27a00a531"); +} + +} // namespace TW::Sui::tests diff --git a/tests/chains/Sui/SignerTests.cpp b/tests/chains/Sui/SignerTests.cpp new file mode 100644 index 00000000000..29c640dfbff --- /dev/null +++ b/tests/chains/Sui/SignerTests.cpp @@ -0,0 +1,65 @@ +// Copyright © 2017-2023 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 "Sui/Signer.h" +#include "Sui/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" + +#include + +namespace TW::Sui::tests { + +TEST(SuiSigner, Transfer) { + // Successfully broadcasted https://explorer.sui.io/transaction/rxLgxcAqgMg8gphp6eCsSGQcdZnwFYx2SRdwEhnAUC4 + Proto::SigningInput input; + auto txMsg = "AAUCLiNiMy/EzosKCk5EZr5QQZmMVLnvAAAAAAAAACDqj/OT+1+qyLZKV4YLw8kpK3/bTZKspTUmh1pBuUfHPLb0crwkV1LQcBARaxER8XhTNJmK7wAAAAAAAAAgaQEguOdXa+m16IM536nsveakQ4u/GYJAc1fpYGGKEvgBQUP35yxF+cEL5qm153kw18dVeuYB6AMAAAAAAAAttQCskZzd41GsNuNxHYMsbbl2aS4jYjMvxM6LCgpORGa+UEGZjFS57wAAAAAAAAAg6o/zk/tfqsi2SleGC8PJKSt/202SrKU1JodaQblHxzwBAAAAAAAAAOgDAAAAAAAA"; + input.mutable_sign_direct_message()->set_unsigned_tx_msg(txMsg); + auto privateKey = PrivateKey(parse_hex("3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(result.unsigned_tx(), "AAUCLiNiMy/EzosKCk5EZr5QQZmMVLnvAAAAAAAAACDqj/OT+1+qyLZKV4YLw8kpK3/bTZKspTUmh1pBuUfHPLb0crwkV1LQcBARaxER8XhTNJmK7wAAAAAAAAAgaQEguOdXa+m16IM536nsveakQ4u/GYJAc1fpYGGKEvgBQUP35yxF+cEL5qm153kw18dVeuYB6AMAAAAAAAAttQCskZzd41GsNuNxHYMsbbl2aS4jYjMvxM6LCgpORGa+UEGZjFS57wAAAAAAAAAg6o/zk/tfqsi2SleGC8PJKSt/202SrKU1JodaQblHxzwBAAAAAAAAAOgDAAAAAAAA"); + ASSERT_EQ(result.signature(), "AIYRmHDpQesfAx3iWBCMwInf3MZ56ZQGnPWNtECFjcSq0ssAgjRW6GLnFCX24tfDNjSm9gjYgoLmn1No15iFJAtqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); +} + +TEST(SuiSigner, TransferNFT) { + // Successfully broadcasted https://explorer.sui.io/transaction/EmnhP9swuoijxYwHMywnXDGCXfFs1QxErsYoyWy9Y15J + Proto::SigningInput input; + std::string unsigned_tx = R"(AAAv0f6HrJCZ/1cuDVuxh1BL12XMeHxkKeZ7Js9grhcB0u8xtTvoOepOHAAAAAAAAAAgJvcpOSvKhM+tHPgGAnp5Pmc8l3wjZhVxK4/BrLu4YAgttQCskZzd41GsNuNxHYMsbbl2aSEnoKw8oAGf/LobCM7RxGurtPZtHAAAAAAAAAAgwk74iUAH9S+cGVXQxAydItvltZ3UK2L0vg1TYgDMPfABAAAAAAAAAOgDAAAAAAAA)"; + input.mutable_sign_direct_message()->set_unsigned_tx_msg(unsigned_tx); + auto privateKey = PrivateKey(parse_hex("3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(result.unsigned_tx(), unsigned_tx); + ASSERT_EQ(result.signature(), "AIjbyuyg9YX0f8/DXB5XZBnUCOqhUrPDbU9/E/FlzwGtDS57cOL/gZwN3vTV1KiOuN0cr0kxypgJpVLKlhd8hgdqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); +} + +TEST(SuiSigner, MoveCall) { + // Successfully broadcasted on: https://explorer.sui.io/transaction/3Gg8AcEfokDnA8m7W58ANmeCr8vkSaPWjXMp9sLMScTj + Proto::SigningInput input; + std::string unsigned_tx = R"(AAIAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAINaXMihjlCd4CQVFRPjcNb7QfYP4wGgQyl1xbplvEKUCA3N1aQh0cmFuc2ZlcgACAQCdB6Mav5rHiXD0rAWTCxS+ENwxMBsAAAAAAAAAINqDfrJUZebPjUi7xcyR3QcQSA9tOLwxThgYaZ1vMfgfABQU2gJ3ToaOYd1F/R6mXryOZdvpRi21AKyRnN3jUaw243EdgyxtuXZppM+mSjYYEQWDcV/7hFRrAE0VtRwbAAAAAAAAACC5nJxYaYJfa9rfbxSikaEFVmHGuXyCIZoZbMpxMwLebAEAAAAAAAAA0AcAAAAAAAA=)"; + input.mutable_sign_direct_message()->set_unsigned_tx_msg(unsigned_tx); + auto privateKey = PrivateKey(parse_hex("3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(result.unsigned_tx(), unsigned_tx); + ASSERT_EQ(result.signature(), "AE8394w/+KOodhLjnKgu21iW0xZur6MA4ajPh31f2xaOI7vs6JHLAHLk5ED3bfJfc5ZehmC6D4DMyrH4F0dA3A1qfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); +} + +TEST(SuiSigner, AddDelegation) { + // Successfully broadcasted on: https://explorer.sui.io/transaction/3Gg8AcEfokDnA8m7W58ANmeCr8vkSaPWjXMp9sLMScTj + Proto::SigningInput input; + std::string unsigned_tx = R"(AAIAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAIEt/p6rXSTjdKP6wJOXyx0c2xsgJ4MJtfxe7qHC34u4UCnN1aV9zeXN0ZW0fcmVxdWVzdF9hZGRfZGVsZWdhdGlvbl9tdWxfY29pbgAEAQEAAAAAAAAAAAAAAAAAAAAAAAAABQEAAAAAAAAAAgIAGSkMV9AFc419O9dL1kez9tzVIOiXzAEAAAAAACAfIePlHHP/+iv++FWQW9ofkVm4S2sFwupGikSq8bNjYwAH1A26NKDn7pJfn9zWaDi1nbntMJfMAQAAAAAAIKfMZAZktdmw36jwg/jcK1TDmrHmSZ/fdkeInO3BSjfWAAkB0AcAAAAAAAAAFAej1I8mhjmcpQTQtiX2J2HZ7y2xLbUArJGc3eNRrDbjcR2DLG25dmlY2RBfTU/P+GL1qpxE8NQrwiLw/JfMAQAAAAAAICs2NJlowCmCnpJ+hja2VwZE5K6yGM/qw0MSRnn9tW2cbgAAAAAAAACghgEAAAAAAA==)"; + input.mutable_sign_direct_message()->set_unsigned_tx_msg(unsigned_tx); + auto privateKey = PrivateKey(parse_hex("3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(result.unsigned_tx(), unsigned_tx); + ASSERT_EQ(result.signature(), "AKSbUoc+F4JGG9i+A8yVYyzcD8BXNV88iSaWpoS5KXUG7ao2pxjyvfUJEYyhWXTxgQazNDnIM1xGhD7zu1GU1wRqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); +} + +} // namespace TW::Sui::tests diff --git a/tests/chains/Sui/TWCoinTypeTests.cpp b/tests/chains/Sui/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..9489907728d --- /dev/null +++ b/tests/chains/Sui/TWCoinTypeTests.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2023 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. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWSuiCoinType, TWCoinType) { + const auto coin = TWCoinTypeSui; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("SWRW1RoMHxnD9NeobgBoC4cXGwp2Hc511CnfWUoTBmo")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x62107e1afefccc7b2267ab74e332c146f5c2ca15")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "sui"); + assertStringsEqual(name, "Sui"); + assertStringsEqual(symbol, "SUI"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 9); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainSui); + assertStringsEqual(txUrl, "https://explorer.sui.io//transaction/SWRW1RoMHxnD9NeobgBoC4cXGwp2Hc511CnfWUoTBmo"); + assertStringsEqual(accUrl, "https://explorer.sui.io//address/0x62107e1afefccc7b2267ab74e332c146f5c2ca15"); +} diff --git a/tests/common/Base64Tests.cpp b/tests/common/Base64Tests.cpp index 1d61102f7ff..339c4859175 100644 --- a/tests/common/Base64Tests.cpp +++ b/tests/common/Base64Tests.cpp @@ -46,6 +46,15 @@ TEST(Base64, decode) { EXPECT_EQ("11ff8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d291b", hex(decoded)); } + + +TEST(Base64, EncodeDecodeSui) { + auto v = "AAIAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAINaXMihjlCd4CQVFRPjcNb7QfYP4wGgQyl1xbplvEKUCA3N1aQh0cmFuc2ZlcgACAQCDlY9/fBVEt0yclyDF8RrjSRBfRRsAAAAAAAAAIJttZrU/26Bim7ku4dwY8d3fdabngn0B6dY/hLKgb6+xABQv0f6HrJCZ/1cuDVuxh1BL12XMeC21AKyRnN3jUaw243EdgyxtuXZpG62iKzFvYdk6RMGXxnoWd8RcfwkUAQAAAAAAACDi9GYNIZ0FXpPPi+zdDUuzHfs6MDoxzPuXGPZJq8ZfOAEAAAAAAAAA0AcAAAAAAAA="; + auto decoded = decode(v); + auto encoded = encode(decoded); + ASSERT_EQ(encoded, v); +} + TEST(Base64, UrlFormat) { const std::string const1 = "11003faa8556289975ec991ac9994dfb613abec4ea000d5094e6379080f594e559b330b8"; diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 34b27fce10e..7f54228c9a1 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -256,6 +256,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeAptos: EXPECT_EQ(address, "0xce2fd04ac9efa74f17595e5785e847a2399d7e637f5e8179244f76191f653276"); break; + case TWCoinTypeSui: + EXPECT_EQ(address, "0xfc93395679dec6ca84d9766be2014b6bc1473f2e"); + break; case TWCoinTypeHedera: EXPECT_EQ(address, "0.0.302a300506032b6570032100ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148"); break; diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp index c5f858bbc04..44441d5b113 100644 --- a/tests/common/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -8,6 +8,7 @@ #include "Bitcoin/Address.h" #include "Bitcoin/CashAddress.h" #include "Bitcoin/SegwitAddress.h" +#include "Sui/Address.h" #include "Coin.h" #include "Ethereum/Address.h" #include "Ethereum/EIP2645.h" @@ -440,6 +441,18 @@ TEST(HDWallet, AptosKey) { } } +TEST(HDWallet, SuiKey) { + const auto derivPath = "m/44'/784'/0'/0'/0'"; + HDWallet wallet = HDWallet("cost add execute system fault long raccoon stone paddle column ketchup smile debate wood marble please jar can goddess magnet axis celery rough gold", ""); + { + const auto privateKey = wallet.getKey(TWCoinTypeSui, DerivationPath(derivPath)); + EXPECT_EQ(hex(privateKey.bytes), "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266"); + auto pubkey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + EXPECT_EQ(hex(pubkey.bytes), "6a7cdeec16a75c0ff6787bc2356109469033022bb10e826c9d443a9f1fc0bd8e"); + EXPECT_EQ(TW::Sui::Address(pubkey).string(), "0x2db500ac919cdde351ac36e3711d832c6db97669"); + } +} + TEST(HDWallet, HederaKey) { // https://github.com/hashgraph/hedera-sdk-js/blob/e0cd39c84ab189d59a6bcedcf16e4102d7bb8beb/packages/cryptography/test/unit/Mnemonic.js#L47 { diff --git a/tests/common/rust/bindgen/WalletCoreRsTests.cpp b/tests/common/rust/bindgen/WalletCoreRsTests.cpp index ce99263e0c1..4293863ec56 100644 --- a/tests/common/rust/bindgen/WalletCoreRsTests.cpp +++ b/tests/common/rust/bindgen/WalletCoreRsTests.cpp @@ -5,7 +5,6 @@ // file LICENSE at the root of the source code distribution tree. #include "rust/bindgen/WalletCoreRSBindgen.h" - #include "gtest/gtest.h" TEST(RustBindgen, MoveParseFunctionArgument) { diff --git a/tools/install-wasm-dependencies b/tools/install-wasm-dependencies index dcf17caa686..31fc852b850 100755 --- a/tools/install-wasm-dependencies +++ b/tools/install-wasm-dependencies @@ -2,7 +2,7 @@ set -e -emsdk_version=3.1.22 +emsdk_version=3.1.30 git clone https://github.com/emscripten-core/emsdk.git diff --git a/walletconsole/lib/Util.cpp b/walletconsole/lib/Util.cpp index 86dd938dc84..a0312d05261 100644 --- a/walletconsole/lib/Util.cpp +++ b/walletconsole/lib/Util.cpp @@ -38,14 +38,13 @@ bool Util::base64Encode(const string& p, string& res) { } bool Util::base64Decode(const string& p, string& res) { - try { - auto dec = Base64::decode(p); - res = TW::hex(dec); - return true; - } catch (exception& ex) { + auto dec = Base64::decode(p); + if (dec.empty()) { _out << "Error while Base64 decode" << endl; return false; } + res = TW::hex(dec); + return true; } bool Util::fileW(const string& fileName, const string& data, [[maybe_unused]] string& res) { diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt index e7ea9f4ca0e..592e636d94c 100644 --- a/wasm/CMakeLists.txt +++ b/wasm/CMakeLists.txt @@ -40,5 +40,5 @@ set_target_properties(${TARGET_NAME} set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_FLAGS "-O2 -sSTRICT -sUSE_BOOST_HEADERS=1" - LINK_FLAGS "--bind --no-entry --closure 1 -O2 -sSTRICT -sASSERTIONS -sMODULARIZE=1 -sALLOW_MEMORY_GROWTH=1 -sDYNAMIC_EXECUTION=0" + LINK_FLAGS "--bind --no-entry --closure 1 -O2 -sSTRICT -sASSERTIONS -sMODULARIZE=1 -sALLOW_MEMORY_GROWTH=1 -sDYNAMIC_EXECUTION=0 -s EXPORTED_FUNCTIONS=['_setThrew']" ) diff --git a/wasm/tests/Blockchain/Sui.test.ts b/wasm/tests/Blockchain/Sui.test.ts new file mode 100644 index 00000000000..49baaea6c84 --- /dev/null +++ b/wasm/tests/Blockchain/Sui.test.ts @@ -0,0 +1,28 @@ +// Copyright © 2017-2023 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. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; +import { TW } from "../../dist"; + +describe("Sui", () => { + it("test sign Sui", () => { + const { PrivateKey, HexCoding, AnySigner, AnyAddress, CoinType } = globalThis.core; + const txDataInput = TW.Sui.Proto.SigningInput.create({ + signDirectMessage: TW.Sui.Proto.SignDirect.create({ + unsignedTxMsg: "AAUCLiNiMy/EzosKCk5EZr5QQZmMVLnvAAAAAAAAACDqj/OT+1+qyLZKV4YLw8kpK3/bTZKspTUmh1pBuUfHPLb0crwkV1LQcBARaxER8XhTNJmK7wAAAAAAAAAgaQEguOdXa+m16IM536nsveakQ4u/GYJAc1fpYGGKEvgBQUP35yxF+cEL5qm153kw18dVeuYB6AMAAAAAAAAttQCskZzd41GsNuNxHYMsbbl2aS4jYjMvxM6LCgpORGa+UEGZjFS57wAAAAAAAAAg6o/zk/tfqsi2SleGC8PJKSt/202SrKU1JodaQblHxzwBAAAAAAAAAOgDAAAAAAAA" + }), + privateKey: HexCoding.decode( + "0x3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266", + ) + }); + const input = TW.Sui.Proto.SigningInput.encode(txDataInput).finish(); + const outputData = AnySigner.sign(input, CoinType.sui); + const output = TW.Sui.Proto.SigningOutput.decode(outputData); + assert.equal(output.signature, "AIYRmHDpQesfAx3iWBCMwInf3MZ56ZQGnPWNtECFjcSq0ssAgjRW6GLnFCX24tfDNjSm9gjYgoLmn1No15iFJAtqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg==") + }); +}); From 588bd84642428db5fd417f41e1bac99e76128492 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 1 Feb 2023 10:52:01 +0100 Subject: [PATCH 086/426] fix(thorchain): fix overflow for big amount (#2901) --- src/THORChain/Swap.cpp | 14 +++++++------- src/THORChain/Swap.h | 9 +++++---- tests/chains/THORChain/SwapTests.cpp | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index cb42e556b81..52eb156d7be 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -109,7 +109,7 @@ SwapBundled SwapBuilder::build(bool shortened) { return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_to_address), .error = "Invalid to address"}; } - uint64_t fromAmountNum = std::stoull(mFromAmount); + uint256_t fromAmountNum = uint256_t(mFromAmount); const auto memo = this->buildMemo(shortened); switch (fromChain) { @@ -164,7 +164,7 @@ std::string SwapBuilder::buildMemo(bool shortened) noexcept { return memo.str(); } -SwapBundled SwapBuilder::buildBitcoin(uint64_t amount, const std::string& memo, Chain fromChain) { +SwapBundled SwapBuilder::buildBitcoin(uint256_t amount, const std::string& memo, Chain fromChain) { auto input = Bitcoin::Proto::SigningInput(); Data out; // Following fields must be set afterwards, before signing ... @@ -187,7 +187,7 @@ SwapBundled SwapBuilder::buildBitcoin(uint64_t amount, const std::string& memo, out.insert(out.end(), serialized.begin(), serialized.end()); return {.out = std::move(out)}; } -SwapBundled SwapBuilder::buildBinance(Proto::Asset fromAsset, uint64_t amount, const std::string& memo) { +SwapBundled SwapBuilder::buildBinance(Proto::Asset fromAsset, uint256_t amount, const std::string& memo) { auto input = Binance::Proto::SigningInput(); Data out; @@ -205,7 +205,7 @@ SwapBundled SwapBuilder::buildBinance(Proto::Asset fromAsset, uint64_t amount, c auto token = Binance::Proto::SendOrder::Token(); token.set_denom(fromAsset.token_id().empty() ? "BNB" : fromAsset.token_id()); - token.set_amount(amount); + token.set_amount(static_cast(amount)); { Binance::Address fromAddressBin; Binance::Address::decode(mFromAddress, fromAddressBin); @@ -226,7 +226,7 @@ SwapBundled SwapBuilder::buildBinance(Proto::Asset fromAsset, uint64_t amount, c return {.out = std::move(out)}; } -SwapBundled SwapBuilder::buildEth(uint64_t amount, const std::string& memo) { +SwapBundled SwapBuilder::buildEth(uint256_t amount, const std::string& memo) { Data out; auto input = Ethereum::Proto::SigningInput(); // EIP-1559 @@ -280,7 +280,7 @@ SwapBundled SwapBuilder::buildEth(uint64_t amount, const std::string& memo) { return {.out = std::move(out)}; } -SwapBundled SwapBuilder::buildAtom(uint64_t amount, const std::string& memo) { +SwapBundled SwapBuilder::buildAtom(uint256_t amount, const std::string& memo) { if (!Cosmos::Address::isValid(mVaultAddress, "cosmos")) { return {.status_code = static_cast(Proto::ErrorCode::Error_Invalid_vault_address), .error = "Invalid vault address: " + mVaultAddress}; } @@ -299,7 +299,7 @@ SwapBundled SwapBuilder::buildAtom(uint64_t amount, const std::string& memo) { auto amountOfTx = message.add_amounts(); amountOfTx->set_denom("uatom"); - amountOfTx->set_amount(std::to_string(amount)); + amountOfTx->set_amount(amount.str()); auto serialized = input.SerializeAsString(); out.insert(out.end(), serialized.begin(), serialized.end()); diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h index b8bedea7a76..926ef3535b1 100644 --- a/src/THORChain/Swap.h +++ b/src/THORChain/Swap.h @@ -8,6 +8,7 @@ #include "Data.h" #include "proto/THORChainSwap.pb.h" +#include "uint256.h" #include #include @@ -49,10 +50,10 @@ class SwapBuilder { std::optional mAffFeeRate{std::nullopt}; std::optional mExtraMemo{std::nullopt}; - SwapBundled buildBitcoin(uint64_t amount, const std::string& memo, Chain fromChain); - SwapBundled buildBinance(Proto::Asset fromAsset, uint64_t amount, const std::string& memo); - SwapBundled buildEth(uint64_t amount, const std::string& memo); - SwapBundled buildAtom(uint64_t amount, const std::string& memo); + SwapBundled buildBitcoin(uint256_t amount, const std::string& memo, Chain fromChain); + SwapBundled buildBinance(Proto::Asset fromAsset, uint256_t amount, const std::string& memo); + SwapBundled buildEth(uint256_t amount, const std::string& memo); + SwapBundled buildAtom(uint256_t amount, const std::string& memo); public: SwapBuilder() noexcept = default; diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index f47bc022a34..372bfd70d9e 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -42,6 +42,25 @@ const auto VaultEth = "0x1091c4De6a3cF09CdA00AbDAeD42c7c3B69C83EC"; const auto VaultBnb = "bnb1n9esxuw8ca7ts8l6w66kdh800s09msvul6vlse"; const auto RouterEth = "0x42A5Ed456650a09Dc10EBc6361A7480fDd61f27B"; +TEST(THORChainSwap, OverflowFixEth) { + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::ETH)); + fromAsset.set_symbol("ETH"); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BTC)); + toAsset.set_symbol("BTC"); + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(Address1Eth) + .toAddress(Address1Btc) + .vault(VaultEth) + .fromAmount("1234000000000000000000") + .toAmountLimit("5285656144") + .build(); + ASSERT_EQ(errorCode, 0); +} + TEST(THORChainSwap, SwapBtcEth) { Proto::Asset fromAsset; fromAsset.set_chain(static_cast(Chain::BTC)); From 940ae76fc79fce8e424339ff6579f12f741aaeda Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 2 Feb 2023 14:26:14 +0100 Subject: [PATCH 087/426] Update CODEOWNERS (#2903) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 61760e1454c..79869e12cc8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,4 +3,4 @@ # @global-owner1 and @global-owner2 will be requested for # review when someone opens a pull request. -* @hewigovens @catenocrypt @milerius +* @hewigovens @rsrbk @milerius From ab369a94deb3ff191dbd7b5373e63d467ce96ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Fri, 3 Feb 2023 11:54:46 +0200 Subject: [PATCH 088/426] Code-wise rebranding of Elrond to MultiversX (#2895) --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- .../TestMultiversXAddress.kt} | 10 ++-- .../TestMultiversXSigner.kt} | 54 +++++++++---------- include/TrustWalletCore/TWBlockchain.h | 2 +- include/TrustWalletCore/TWCoinType.h | 2 +- registry.json | 5 +- src/Coin.cpp | 6 +-- src/{Elrond => MultiversX}/Address.cpp | 4 +- src/{Elrond => MultiversX}/Address.h | 17 +++--- src/{Elrond => MultiversX}/Codec.cpp | 6 +-- src/{Elrond => MultiversX}/Codec.h | 6 +-- src/{Elrond => MultiversX}/Entry.cpp | 6 +-- src/{Elrond => MultiversX}/Entry.h | 6 +-- src/{Elrond => MultiversX}/GasEstimator.cpp | 4 +- src/{Elrond => MultiversX}/GasEstimator.h | 2 +- src/{Elrond => MultiversX}/NetworkConfig.cpp | 4 +- src/{Elrond => MultiversX}/NetworkConfig.h | 17 +++--- src/{Elrond => MultiversX}/Serialization.cpp | 6 +-- src/{Elrond => MultiversX}/Serialization.h | 4 +- src/{Elrond => MultiversX}/Signer.cpp | 4 +- src/{Elrond => MultiversX}/Signer.h | 8 +-- src/{Elrond => MultiversX}/Transaction.cpp | 4 +- src/{Elrond => MultiversX}/Transaction.h | 4 +- .../TransactionFactory.cpp | 4 +- .../TransactionFactory.h | 12 +++-- src/proto/{Elrond.proto => MultiversX.proto} | 5 +- ...lrondTests.swift => MultiversXTests.swift} | 54 +++++++++---------- swift/Tests/CoinAddressDerivationTests.swift | 2 +- .../{Elrond => MultiversX}/AddressTests.cpp | 18 +++---- .../SerializationTests.cpp | 12 ++--- .../{Elrond => MultiversX}/SignerTests.cpp | 43 ++++++++------- .../TWAnySignerTests.cpp | 16 +++--- .../TWCoinTypeTests.cpp | 20 +++---- .../{Elrond => MultiversX}/TestAccounts.h | 2 +- .../TransactionFactoryTests.cpp | 18 +++---- tests/common/CoinAddressDerivationTests.cpp | 2 +- tests/common/CoinAddressValidationTests.cpp | 6 +-- tests/interface/TWAnyAddressTests.cpp | 4 +- tests/interface/TWCoinTypeTests.cpp | 4 +- tests/interface/TWHDWalletTests.cpp | 6 +-- tests/interface/TWHRPTests.cpp | 6 +-- 41 files changed, 210 insertions(+), 207 deletions(-) rename android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/{elrond/TestElrondAddress.kt => multiversx/TestMultiversXAddress.kt} (83%) rename android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/{elrond/TestElrondSigner.kt => multiversx/TestMultiversXSigner.kt} (81%) rename src/{Elrond => MultiversX}/Address.cpp (89%) rename src/{Elrond => MultiversX}/Address.h (76%) rename src/{Elrond => MultiversX}/Codec.cpp (90%) rename src/{Elrond => MultiversX}/Codec.h (89%) rename src/{Elrond => MultiversX}/Entry.cpp (92%) rename src/{Elrond => MultiversX}/Entry.h (90%) rename src/{Elrond => MultiversX}/GasEstimator.cpp (96%) rename src/{Elrond => MultiversX}/GasEstimator.h (95%) rename src/{Elrond => MultiversX}/NetworkConfig.cpp (97%) rename src/{Elrond => MultiversX}/NetworkConfig.h (90%) rename src/{Elrond => MultiversX}/Serialization.cpp (88%) rename src/{Elrond => MultiversX}/Serialization.h (90%) rename src/{Elrond => MultiversX}/Signer.cpp (96%) rename src/{Elrond => MultiversX}/Signer.h (81%) rename src/{Elrond => MultiversX}/Transaction.cpp (89%) rename src/{Elrond => MultiversX}/Transaction.h (91%) rename src/{Elrond => MultiversX}/TransactionFactory.cpp (99%) rename src/{Elrond => MultiversX}/TransactionFactory.h (94%) rename src/proto/{Elrond.proto => MultiversX.proto} (93%) rename swift/Tests/Blockchains/{ElrondTests.swift => MultiversXTests.swift} (82%) rename tests/chains/{Elrond => MultiversX}/AddressTests.cpp (86%) rename tests/chains/{Elrond => MultiversX}/SerializationTests.cpp (87%) rename tests/chains/{Elrond => MultiversX}/SignerTests.cpp (95%) rename tests/chains/{Elrond => MultiversX}/TWAnySignerTests.cpp (89%) rename tests/chains/{Elrond => MultiversX}/TWCoinTypeTests.cpp (69%) rename tests/chains/{Elrond => MultiversX}/TestAccounts.h (90%) rename tests/chains/{Elrond => MultiversX}/TransactionFactoryTests.cpp (94%) 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 84ed86b221f..a5958c59d15 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 @@ -94,7 +94,7 @@ class CoinAddressDerivationTests { CARDANO -> assertEquals("addr1qyr8jjfnypp95eq74aqzn7ss687ehxclgj7mu6gratmg3mul2040vt35dypp042awzsjk5xm3zr3zm5qh7454uwdv08s84ray2", address) NEO -> assertEquals("AT6w7PJvwPcSqHvtbNBY2aHPDv12eW5Uuf", address) FILECOIN -> assertEquals("f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori", address) - ELROND -> assertEquals("erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8", address) + MULTIVERSX -> assertEquals("erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8", address) BANDCHAIN -> assertEquals("band1624hqgend0s3d94z68fyka2y5jak6vd7u0l50r", address) SMARTCHAINLEGACY -> assertEquals("0x49784f90176D8D9d4A3feCDE7C1373dAAb5b13b8", address) OASIS -> assertEquals("oasis1qzcpavvmuw280dk0kd4lxjhtpf0u3ll27yf7sqps", address) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXAddress.kt similarity index 83% rename from android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt rename to android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXAddress.kt index e8304e3deeb..6cb38fef175 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXAddress.kt @@ -4,7 +4,7 @@ // 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 +package com.trustwallet.core.app.blockchains.multiversx import com.trustwallet.core.app.utils.toHex import com.trustwallet.core.app.utils.toHexByteArray @@ -12,7 +12,7 @@ import org.junit.Assert.assertEquals import org.junit.Test import wallet.core.jni.* -class TestElrondAddress { +class TestMultiversXAddress { init { System.loadLibrary("TrustWalletCore") @@ -26,7 +26,7 @@ class TestElrondAddress { fun testAddressFromPrivateKey() { val key = PrivateKey(aliceSeedHex.toHexByteArray()) val pubKey = key.publicKeyEd25519 - val address = AnyAddress(pubKey, CoinType.ELROND) + val address = AnyAddress(pubKey, CoinType.MULTIVERSX) assertEquals(alicePubKeyHex, pubKey.data().toHex()) assertEquals(aliceBech32, address.description()) @@ -35,14 +35,14 @@ class TestElrondAddress { @Test fun testAddressFromPublicKey() { val pubKey = PublicKey(alicePubKeyHex.toHexByteArray(), PublicKeyType.ED25519) - val address = AnyAddress(pubKey, CoinType.ELROND) + val address = AnyAddress(pubKey, CoinType.MULTIVERSX) assertEquals(aliceBech32, address.description()) } @Test fun testAddressFromString() { - val address = AnyAddress(aliceBech32, CoinType.ELROND) + val address = AnyAddress(aliceBech32, CoinType.MULTIVERSX) assertEquals(aliceBech32, address.description()) } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXSigner.kt similarity index 81% rename from android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt rename to android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXSigner.kt index a3597e4deb0..72dec595a74 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXSigner.kt @@ -4,7 +4,7 @@ // 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 +package com.trustwallet.core.app.blockchains.multiversx import com.google.protobuf.ByteString import com.trustwallet.core.app.utils.toHexByteArray @@ -13,9 +13,9 @@ 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 +import wallet.core.jni.proto.MultiversX -class TestElrondSigner { +class TestMultiversXSigner { init { System.loadLibrary("TrustWalletCore") @@ -29,20 +29,20 @@ class TestElrondSigner { fun signGenericAction() { val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) - val accounts = Elrond.Accounts.newBuilder() + val accounts = MultiversX.Accounts.newBuilder() .setSenderNonce(7) .setSender(aliceBech32) .setReceiver(bobBech32) .build() - val genericAction = Elrond.GenericAction.newBuilder() + val genericAction = MultiversX.GenericAction.newBuilder() .setAccounts(accounts) .setValue("0") .setData("foo") .setVersion(1) .build() - val signingInput = Elrond.SigningInput.newBuilder() + val signingInput = MultiversX.SigningInput.newBuilder() .setGenericAction(genericAction) .setGasPrice(1000000000) .setGasLimit(50000) @@ -50,7 +50,7 @@ class TestElrondSigner { .setPrivateKey(privateKey) .build() - val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val output = AnySigner.sign(signingInput, CoinType.MULTIVERSX, MultiversX.SigningOutput.parser()) val expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700" assertEquals(expectedSignature, output.signature) @@ -62,20 +62,20 @@ class TestElrondSigner { // Successfully broadcasted https://explorer.multiversx.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) - val accounts = Elrond.Accounts.newBuilder() + val accounts = MultiversX.Accounts.newBuilder() .setSenderNonce(6) .setSender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa") .setReceiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r") .build() - val genericAction = Elrond.GenericAction.newBuilder() + val genericAction = MultiversX.GenericAction.newBuilder() .setAccounts(accounts) .setValue("0") .setData("unDelegate@0de0b6b3a7640000") .setVersion(1) .build() - val signingInput = Elrond.SigningInput.newBuilder() + val signingInput = MultiversX.SigningInput.newBuilder() .setGenericAction(genericAction) .setGasPrice(1000000000) .setGasLimit(12000000) @@ -83,7 +83,7 @@ class TestElrondSigner { .setPrivateKey(privateKey) .build() - val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val output = AnySigner.sign(signingInput, CoinType.MULTIVERSX, MultiversX.SigningOutput.parser()) val expectedSignature = "89f9683af92f7b835bff4e1d0dbfcff5245b3367df4d23538eb799e0ad0a90be29ac3bd3598ce55b35b35ebef68bfa5738eed39fd01adc33476f65bd1b966e0b" assertEquals(expectedSignature, output.signature) @@ -95,20 +95,20 @@ class TestElrondSigner { // Successfully broadcasted https://explorer.multiversx.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) - val accounts = Elrond.Accounts.newBuilder() + val accounts = MultiversX.Accounts.newBuilder() .setSenderNonce(1) .setSender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa") .setReceiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r") .build() - val genericAction = Elrond.GenericAction.newBuilder() + val genericAction = MultiversX.GenericAction.newBuilder() .setAccounts(accounts) .setValue("1") .setData("delegate") .setVersion(1) .build() - val signingInput = Elrond.SigningInput.newBuilder() + val signingInput = MultiversX.SigningInput.newBuilder() .setGenericAction(genericAction) .setGasPrice(1000000000) .setGasLimit(12000000) @@ -116,7 +116,7 @@ class TestElrondSigner { .setPrivateKey(privateKey) .build() - val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val output = AnySigner.sign(signingInput, CoinType.MULTIVERSX, MultiversX.SigningOutput.parser()) val expectedSignature = "3b9164d47a4e3c0330ae387cd29ba6391f9295acf5e43a16a4a2611645e66e5fa46bf22294ca68fe1948adf45cec8cb47b8792afcdb248bd9adec7c6e6c27108" assertEquals(expectedSignature, output.signature) @@ -127,24 +127,24 @@ class TestElrondSigner { fun signEGLDTransfer() { val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) - val accounts = Elrond.Accounts.newBuilder() + val accounts = MultiversX.Accounts.newBuilder() .setSenderNonce(7) .setSender(aliceBech32) .setReceiver(bobBech32) .build() - val transfer = Elrond.EGLDTransfer.newBuilder() + val transfer = MultiversX.EGLDTransfer.newBuilder() .setAccounts(accounts) .setAmount("1000000000000000000") .build() - val signingInput = Elrond.SigningInput.newBuilder() + val signingInput = MultiversX.SigningInput.newBuilder() .setEgldTransfer(transfer) .setChainId("1") .setPrivateKey(privateKey) .build() - val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val output = AnySigner.sign(signingInput, CoinType.MULTIVERSX, MultiversX.SigningOutput.parser()) val expectedSignature = "7e1c4c63b88ea72dcf7855a54463b1a424eb357ac3feb4345221e512ce07c7a50afb6d7aec6f480b554e32cf2037082f3bc17263d1394af1f3ef240be53c930b" assertEquals(expectedSignature, output.signature) @@ -155,25 +155,25 @@ class TestElrondSigner { fun signESDTTransfer() { val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) - val accounts = Elrond.Accounts.newBuilder() + val accounts = MultiversX.Accounts.newBuilder() .setSenderNonce(7) .setSender(aliceBech32) .setReceiver(bobBech32) .build() - val transfer = Elrond.ESDTTransfer.newBuilder() + val transfer = MultiversX.ESDTTransfer.newBuilder() .setAccounts(accounts) .setTokenIdentifier("MYTOKEN-1234") .setAmount("10000000000000") .build() - val signingInput = Elrond.SigningInput.newBuilder() + val signingInput = MultiversX.SigningInput.newBuilder() .setEsdtTransfer(transfer) .setChainId("1") .setPrivateKey(privateKey) .build() - val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val output = AnySigner.sign(signingInput, CoinType.MULTIVERSX, MultiversX.SigningOutput.parser()) val expectedSignature = "9add6d9ac3f1a1fddb07b934e8a73cad3b8c232bdf29d723c1b38ad619905f03e864299d06eb3fe3bbb48a9f1d9b7f14e21dc5eaffe0c87f5718ad0c4198bb0c" val expectedData = "RVNEVFRyYW5zZmVyQDRkNTk1NDRmNGI0NTRlMmQzMTMyMzMzNEAwOTE4NGU3MmEwMDA=" @@ -185,26 +185,26 @@ class TestElrondSigner { fun signESDTNFTTransfer() { val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) - val accounts = Elrond.Accounts.newBuilder() + val accounts = MultiversX.Accounts.newBuilder() .setSenderNonce(7) .setSender(aliceBech32) .setReceiver(bobBech32) .build() - val transfer = Elrond.ESDTNFTTransfer.newBuilder() + val transfer = MultiversX.ESDTNFTTransfer.newBuilder() .setAccounts(accounts) .setTokenCollection("LKMEX-aab910") .setTokenNonce(4) .setAmount("184300000000000000") .build() - val signingInput = Elrond.SigningInput.newBuilder() + val signingInput = MultiversX.SigningInput.newBuilder() .setEsdtnftTransfer(transfer) .setChainId("1") .setPrivateKey(privateKey) .build() - val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val output = AnySigner.sign(signingInput, CoinType.MULTIVERSX, MultiversX.SigningOutput.parser()) val expectedSignature = "cc935685d5b31525e059a16a832cba98dee751983a5a93de4198f6553a2c55f5f1e0b4300fe9077376fa754546da0b0f6697e66462101a209aafd0fc775ab60a" val expectedData = "RVNEVE5GVFRyYW5zZmVyQDRjNGI0ZDQ1NTgyZDYxNjE2MjM5MzEzMEAwNEAwMjhlYzNkZmEwMWFjMDAwQDgwNDlkNjM5ZTVhNjk4MGQxY2QyMzkyYWJjY2U0MTAyOWNkYTc0YTE1NjM1MjNhMjAyZjA5NjQxY2MyNjE4Zjg=" diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index c93bab5e2b9..36daed2499f 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -44,7 +44,7 @@ enum TWBlockchain { TWBlockchainCardano = 30, TWBlockchainNEO = 31, TWBlockchainFilecoin = 32, - TWBlockchainElrondNetwork = 33, + TWBlockchainMultiversX = 33, TWBlockchainOasisNetwork = 34, TWBlockchainDecred = 35, // Bitcoin TWBlockchainZcash = 36, // Bitcoin diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 22a3540c265..0337ee4c6b0 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -88,7 +88,7 @@ enum TWCoinType { TWCoinTypeKusama = 434, TWCoinTypePolkadot = 354, TWCoinTypeFilecoin = 461, - TWCoinTypeElrond = 508, + TWCoinTypeMultiversX = 508, TWCoinTypeBandChain = 494, TWCoinTypeSmartChainLegacy = 10000714, TWCoinTypeSmartChain = 20000714, diff --git a/registry.json b/registry.json index 07081485815..869c99c92bc 100644 --- a/registry.json +++ b/registry.json @@ -1405,12 +1405,11 @@ }, { "id": "elrond", - "name": "Elrond", - "displayName": "MultiversX", + "name": "MultiversX", "coinId": 508, "symbol": "eGLD", "decimals": 18, - "blockchain": "ElrondNetwork", + "blockchain": "MultiversX", "derivation": [ { "path": "m/44'/508'/0'/0'/0'" diff --git a/src/Coin.cpp b/src/Coin.cpp index 167fb0026b2..22930188aaa 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -23,7 +23,7 @@ #include "Cosmos/Entry.h" #include "Decred/Entry.h" #include "EOS/Entry.h" -#include "Elrond/Entry.h" +#include "MultiversX/Entry.h" #include "Ethereum/Entry.h" #include "Everscale/Entry.h" #include "FIO/Entry.h" @@ -72,7 +72,7 @@ Binance::Entry binanceDP; Bitcoin::Entry bitcoinDP; Cardano::Entry cardanoDP; Cosmos::Entry cosmosDP; -Elrond::Entry elrondDP; +MultiversX::Entry multiversxDP; EOS::Entry eosDP; Ethereum::Entry ethereumDP; Decred::Entry decredDP; @@ -148,7 +148,7 @@ CoinEntry* coinDispatcher(TWCoinType coinType) { case TWBlockchainCardano: entry = &cardanoDP; break; case TWBlockchainNEO: entry = &neoDP; break; case TWBlockchainFilecoin: entry = &filecoinDP; break; - case TWBlockchainElrondNetwork: entry = &elrondDP; break; + case TWBlockchainMultiversX: entry = &multiversxDP; break; case TWBlockchainOasisNetwork: entry = &oasisDP; break; case TWBlockchainDecred: entry = &decredDP; break; case TWBlockchainGroestlcoin: entry = &groestlcoinDP; break; diff --git a/src/Elrond/Address.cpp b/src/MultiversX/Address.cpp similarity index 89% rename from src/Elrond/Address.cpp rename to src/MultiversX/Address.cpp index 581f95b1e9e..eb6b8a574b1 100644 --- a/src/Elrond/Address.cpp +++ b/src/MultiversX/Address.cpp @@ -8,7 +8,7 @@ #include "Address.h" -namespace TW::Elrond { +namespace TW::MultiversX { const std::string Address::hrp = HRP_ELROND; @@ -16,4 +16,4 @@ bool Address::isValid(const std::string& string) { return Bech32Address::isValid(string, hrp); } -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/Address.h b/src/MultiversX/Address.h similarity index 76% rename from src/Elrond/Address.h rename to src/MultiversX/Address.h index a638b4af8bd..7827cf1244b 100644 --- a/src/Elrond/Address.h +++ b/src/MultiversX/Address.h @@ -7,32 +7,35 @@ #pragma once #include "Data.h" -#include "../PublicKey.h" #include "../Bech32Address.h" +#include "../PublicKey.h" #include -namespace TW::Elrond { +namespace TW::MultiversX { class Address : public Bech32Address { - public: +public: /// The human-readable part of the address, as defined in "registry.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) {} + Address() + : Bech32Address(hrp) {} /// Initializes an address with a key hash. - Address(Data keyHash) : Bech32Address(hrp, keyHash) {} + Address(Data keyHash) + : Bech32Address(hrp, keyHash) {} /// Initializes an address with a public key. - Address(const PublicKey& publicKey) : Bech32Address(hrp, publicKey.bytes) {} + Address(const PublicKey& publicKey) + : Bech32Address(hrp, publicKey.bytes) {} static bool decode(const std::string& addr, Address& obj_out) { return Bech32Address::decode(addr, obj_out, hrp); } }; -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/Codec.cpp b/src/MultiversX/Codec.cpp similarity index 90% rename from src/Elrond/Codec.cpp rename to src/MultiversX/Codec.cpp index a76328f8b39..d66fa38e706 100644 --- a/src/Elrond/Codec.cpp +++ b/src/MultiversX/Codec.cpp @@ -9,7 +9,7 @@ #include "HexCoding.h" #include "uint256.h" -namespace TW::Elrond { +namespace TW::MultiversX { std::string Codec::encodeString(const std::string& value) { std::string encoded = hex(TW::data(value)); @@ -25,7 +25,7 @@ std::string Codec::encodeBigInt(const std::string& value) { return encodeBigInt(uint256_t(value)); } -// For reference, see https://docs.multiversx.com/developers/developer-reference/elrond-serialization-format/#arbitrary-width-big-numbers. +// For reference, see https://docs.multiversx.com/developers/developer-reference/serialization-format#arbitrary-width-big-numbers. std::string Codec::encodeBigInt(uint256_t value) { std::string encoded = hex(store(value)); return encoded; @@ -42,4 +42,4 @@ std::string Codec::encodeAddress(const Address& address) { return encoded; } -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/Codec.h b/src/MultiversX/Codec.h similarity index 89% rename from src/Elrond/Codec.h rename to src/MultiversX/Codec.h index 18d71f3f223..587f3c70a17 100644 --- a/src/Elrond/Codec.h +++ b/src/MultiversX/Codec.h @@ -9,9 +9,9 @@ #include "Address.h" #include "uint256.h" -namespace TW::Elrond { +namespace TW::MultiversX { -/// A stripped-down variant of the Elrond codec. +/// A stripped-down variant of the MultiversX codec. /// For reference, see: /// - https://docs.multiversx.com/developers/developer-reference/overview /// - https://github.com/multiversx/mx-sdk-erdjs/tree/main/src/smartcontracts/codec @@ -26,4 +26,4 @@ class Codec { static std::string encodeAddress(const Address& address); }; -} // namespace +} // namespace TW::MultiversX diff --git a/src/Elrond/Entry.cpp b/src/MultiversX/Entry.cpp similarity index 92% rename from src/Elrond/Entry.cpp rename to src/MultiversX/Entry.cpp index 6cd504b6008..fef968916f8 100644 --- a/src/Elrond/Entry.cpp +++ b/src/MultiversX/Entry.cpp @@ -12,7 +12,7 @@ using namespace TW; using namespace std; -namespace TW::Elrond { +namespace TW::MultiversX { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { @@ -25,7 +25,7 @@ std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicK Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { Address addr; - if (!Elrond::Address::decode(address, addr)) { + if (!MultiversX::Address::decode(address, addr)) { return Data(); } return addr.getKeyHash(); @@ -39,4 +39,4 @@ string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json return Signer::signJSON(json, key); } -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/Entry.h b/src/MultiversX/Entry.h similarity index 90% rename from src/Elrond/Entry.h rename to src/MultiversX/Entry.h index df2a0967e7f..bd5ff2a1409 100644 --- a/src/Elrond/Entry.h +++ b/src/MultiversX/Entry.h @@ -8,9 +8,9 @@ #include "../CoinEntry.h" -namespace TW::Elrond { +namespace TW::MultiversX { -/// Entry point for implementation of Elrond coin. +/// Entry point for implementation of MultiversX coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public CoinEntry { public: @@ -22,4 +22,4 @@ class Entry final : public CoinEntry { std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/GasEstimator.cpp b/src/MultiversX/GasEstimator.cpp similarity index 96% rename from src/Elrond/GasEstimator.cpp rename to src/MultiversX/GasEstimator.cpp index 6db7cbc9580..d57853686ff 100644 --- a/src/Elrond/GasEstimator.cpp +++ b/src/MultiversX/GasEstimator.cpp @@ -6,7 +6,7 @@ #include "GasEstimator.h" -namespace TW::Elrond { +namespace TW::MultiversX { // Additional gas to account for eventual increases in gas requirements (thus avoid breaking changes in clients of TW-core). const uint64_t ADDITIONAL_GAS_FOR_ESDT_TRANSFER = 100000; @@ -47,4 +47,4 @@ uint64_t GasEstimator::forESDTNFTTransfer(size_t dataLength) const { return gasLimit; } -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/GasEstimator.h b/src/MultiversX/GasEstimator.h similarity index 95% rename from src/Elrond/GasEstimator.h rename to src/MultiversX/GasEstimator.h index 4c14ed434d6..77f848bd9db 100644 --- a/src/Elrond/GasEstimator.h +++ b/src/MultiversX/GasEstimator.h @@ -9,7 +9,7 @@ #include #include "NetworkConfig.h" -namespace TW::Elrond { +namespace TW::MultiversX { class GasEstimator { NetworkConfig networkConfig; diff --git a/src/Elrond/NetworkConfig.cpp b/src/MultiversX/NetworkConfig.cpp similarity index 97% rename from src/Elrond/NetworkConfig.cpp rename to src/MultiversX/NetworkConfig.cpp index ba514e9183f..8b71350b48f 100644 --- a/src/Elrond/NetworkConfig.cpp +++ b/src/MultiversX/NetworkConfig.cpp @@ -8,7 +8,7 @@ #include -namespace TW::Elrond { +namespace TW::MultiversX { NetworkConfig::NetworkConfig() : chainId("1") /* Mainnet */ { @@ -82,4 +82,4 @@ NetworkConfig NetworkConfig::GetByTimestamp(uint64_t timestamp) { return networkConfig; } -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/NetworkConfig.h b/src/MultiversX/NetworkConfig.h similarity index 90% rename from src/Elrond/NetworkConfig.h rename to src/MultiversX/NetworkConfig.h index 719335b17ed..5df96a75a1a 100644 --- a/src/Elrond/NetworkConfig.h +++ b/src/MultiversX/NetworkConfig.h @@ -8,7 +8,7 @@ #include -namespace TW::Elrond { +namespace TW::MultiversX { /// A "NetworkConfig" object holds the network parameters relevant to creating transactions (e.g. minimum gas limit, minimum gas price). class NetworkConfig { @@ -19,26 +19,27 @@ class NetworkConfig { uint32_t minGasLimit; uint64_t minGasPrice; - /// GasSchedule entries of interest (only one at this moment), according to: https://github.com/ElrondNetwork/elrond-config-mainnet/blob/master/gasSchedules. - /// Here, for the sake of simplicity, we define the gas costs of interest directly in the class "NetworkConfig" + /// GasSchedule entries of interest (only one at this moment), according to: https://github.com/multiversx/mx-chain-mainnet-config/blob/master/gasSchedules. + /// Here, for the sake of simplicity, we define the gas costs of interest directly in the class "NetworkConfig" /// (that is, without defining extra nested structures such as "GasSchedule" and "BuiltInCosts"). uint32_t gasCostESDTTransfer; uint32_t gasCostESDTNFTTransfer; + public: NetworkConfig(); const std::string& getChainId() const; void setChainId(const std::string& value); - + uint32_t getGasPerDataByte() const; void setGasPerDataByte(uint32_t value); - + uint32_t getMinGasLimit() const; void setMinGasLimit(uint32_t value); - + uint64_t getMinGasPrice() const; void setMinGasPrice(uint64_t value); - + uint32_t getGasCostESDTTransfer() const; void setGasCostESDTTransfer(uint32_t value); @@ -51,4 +52,4 @@ class NetworkConfig { static NetworkConfig GetByTimestamp(uint64_t timestamp); }; -} // namespace +} // namespace TW::MultiversX diff --git a/src/Elrond/Serialization.cpp b/src/MultiversX/Serialization.cpp similarity index 88% rename from src/Elrond/Serialization.cpp rename to src/MultiversX/Serialization.cpp index ffe2241bd08..08566e01e21 100644 --- a/src/Elrond/Serialization.cpp +++ b/src/MultiversX/Serialization.cpp @@ -36,7 +36,7 @@ template using sorted_map = std::map; using sorted_json = nlohmann::basic_json; -sorted_json preparePayload(const Elrond::Transaction& transaction) { +sorted_json preparePayload(const MultiversX::Transaction& transaction) { using namespace nlohmann; sorted_json payload{ {"nonce", json(transaction.nonce)}, @@ -69,12 +69,12 @@ sorted_json preparePayload(const Elrond::Transaction& transaction) { return payload; } -std::string Elrond::serializeTransaction(const Elrond::Transaction& transaction) { +std::string MultiversX::serializeTransaction(const MultiversX::Transaction& transaction) { sorted_json payload = preparePayload(transaction); return payload.dump(); } -std::string Elrond::serializeSignedTransaction(const Elrond::Transaction& transaction, std::string signature) { +std::string MultiversX::serializeSignedTransaction(const MultiversX::Transaction& transaction, std::string signature) { sorted_json payload = preparePayload(transaction); payload["signature"] = nlohmann::json(signature); return payload.dump(); diff --git a/src/Elrond/Serialization.h b/src/MultiversX/Serialization.h similarity index 90% rename from src/Elrond/Serialization.h rename to src/MultiversX/Serialization.h index 64023d47213..0058df7d38a 100644 --- a/src/Elrond/Serialization.h +++ b/src/MultiversX/Serialization.h @@ -10,7 +10,7 @@ #include "Transaction.h" #include -namespace TW::Elrond { +namespace TW::MultiversX { using string = std::string; using json = nlohmann::json; @@ -18,4 +18,4 @@ using json = nlohmann::json; string serializeTransaction(const Transaction& transaction); string serializeSignedTransaction(const Transaction& transaction, string encodedSignature); -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/Signer.cpp b/src/MultiversX/Signer.cpp similarity index 96% rename from src/Elrond/Signer.cpp rename to src/MultiversX/Signer.cpp index f26e6c0460c..a8ec5f20183 100644 --- a/src/Elrond/Signer.cpp +++ b/src/MultiversX/Signer.cpp @@ -12,7 +12,7 @@ #include -namespace TW::Elrond { +namespace TW::MultiversX { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { TransactionFactory factory; @@ -39,4 +39,4 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { return output.encoded(); } -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/Signer.h b/src/MultiversX/Signer.h similarity index 81% rename from src/Elrond/Signer.h rename to src/MultiversX/Signer.h index df43bf1295d..57260a34ac6 100644 --- a/src/Elrond/Signer.h +++ b/src/MultiversX/Signer.h @@ -8,11 +8,11 @@ #include "Data.h" #include "../PrivateKey.h" -#include "../proto/Elrond.pb.h" +#include "../proto/MultiversX.pb.h" -namespace TW::Elrond { +namespace TW::MultiversX { -/// Helper class that performs Elrond transaction signing. +/// Helper class that performs MultiversX transaction signing. class Signer { public: /// Hide default constructor @@ -25,4 +25,4 @@ class Signer { static std::string signJSON(const std::string& json, const Data& key); }; -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/Transaction.cpp b/src/MultiversX/Transaction.cpp similarity index 89% rename from src/Elrond/Transaction.cpp rename to src/MultiversX/Transaction.cpp index 24c8373e9c5..92f6db9b403 100644 --- a/src/Elrond/Transaction.cpp +++ b/src/MultiversX/Transaction.cpp @@ -6,11 +6,11 @@ #include "Transaction.h" -namespace TW::Elrond { +namespace TW::MultiversX { Transaction::Transaction() : nonce(0), sender(""), senderUsername(""), receiver(""), receiverUsername(""), value("0"), data(""), gasPrice(0), gasLimit(0), chainID(""), version(0), options(0) { } -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/Transaction.h b/src/MultiversX/Transaction.h similarity index 91% rename from src/Elrond/Transaction.h rename to src/MultiversX/Transaction.h index a98e7435360..e40885b78bc 100644 --- a/src/Elrond/Transaction.h +++ b/src/MultiversX/Transaction.h @@ -8,7 +8,7 @@ #include -namespace TW::Elrond { +namespace TW::MultiversX { class Transaction { public: @@ -28,4 +28,4 @@ class Transaction { Transaction(); }; -} // namespace +} // namespace TW::MultiversX diff --git a/src/Elrond/TransactionFactory.cpp b/src/MultiversX/TransactionFactory.cpp similarity index 99% rename from src/Elrond/TransactionFactory.cpp rename to src/MultiversX/TransactionFactory.cpp index 4e3a271165e..58822f03944 100644 --- a/src/Elrond/TransactionFactory.cpp +++ b/src/MultiversX/TransactionFactory.cpp @@ -8,7 +8,7 @@ #include "Codec.h" -namespace TW::Elrond { +namespace TW::MultiversX { const int TX_VERSION = 1; @@ -148,4 +148,4 @@ std::string TransactionFactory::prepareFunctionCall(const std::string& function, return result; } -} // namespace TW::Elrond +} // namespace TW::MultiversX diff --git a/src/Elrond/TransactionFactory.h b/src/MultiversX/TransactionFactory.h similarity index 94% rename from src/Elrond/TransactionFactory.h rename to src/MultiversX/TransactionFactory.h index db7ec70e19c..5b66df1ecfd 100644 --- a/src/Elrond/TransactionFactory.h +++ b/src/MultiversX/TransactionFactory.h @@ -6,26 +6,27 @@ #pragma once -#include "../proto/Elrond.pb.h" #include "Address.h" -#include "NetworkConfig.h" #include "GasEstimator.h" +#include "NetworkConfig.h" #include "Transaction.h" #include "uint256.h" +#include "../proto/MultiversX.pb.h" -namespace TW::Elrond { +namespace TW::MultiversX { /// Creates specific transaction objects, wrt. the provided "NetworkConfig". class TransactionFactory { private: NetworkConfig networkConfig; GasEstimator gasEstimator; + public: TransactionFactory(); TransactionFactory(const NetworkConfig& networkConfig); /// Creates the appropriate transaction object, with respect to the "oneof" field (substructure) of Proto::SigningInput. - Transaction create(const Proto::SigningInput &input); + Transaction create(const Proto::SigningInput& input); Transaction fromGenericAction(const Proto::SigningInput& input); @@ -47,6 +48,7 @@ class TransactionFactory { /// /// The fields "token_collection" and "token_nonce" are found as well in the HTTP response of the API call (as "collection" and "nonce", respectively). Transaction fromESDTNFTTransfer(const Proto::SigningInput& input); + private: uint64_t coalesceGasLimit(uint64_t providedGasLimit, uint64_t estimatedGasLimit); uint64_t coalesceGasPrice(uint64_t gasPrice); @@ -54,4 +56,4 @@ class TransactionFactory { std::string prepareFunctionCall(const std::string& function, std::initializer_list arguments); }; -} // namespace +} // namespace TW::MultiversX diff --git a/src/proto/Elrond.proto b/src/proto/MultiversX.proto similarity index 93% rename from src/proto/Elrond.proto rename to src/proto/MultiversX.proto index 9951055fde1..42dfcd26752 100644 --- a/src/proto/Elrond.proto +++ b/src/proto/MultiversX.proto @@ -6,7 +6,7 @@ syntax = "proto3"; -package TW.Elrond.Proto; +package TW.MultiversX.Proto; option java_package = "wallet.core.jni.proto"; // Generic action. Using one of the more specific actions (e.g. transfers, see below) is recommended. @@ -24,8 +24,7 @@ message GenericAction { uint32 version = 4; // Currently, the "options" field should be ignored (not set) by applications using TW Core. - // In the future, TW Core will handle specific transaction options - // (such as the "SignedWithHash" flag, as seen in https://github.com/ElrondNetwork/elrond-go-core/blob/main/core/versioning/txVersionChecker.go) + // In the future, TW Core will handle specific transaction options // when building and signing transactions. uint32 options = 5; } diff --git a/swift/Tests/Blockchains/ElrondTests.swift b/swift/Tests/Blockchains/MultiversXTests.swift similarity index 82% rename from swift/Tests/Blockchains/ElrondTests.swift rename to swift/Tests/Blockchains/MultiversXTests.swift index fa6a64ee3ca..327e3ab6b80 100644 --- a/swift/Tests/Blockchains/ElrondTests.swift +++ b/swift/Tests/Blockchains/MultiversXTests.swift @@ -7,7 +7,7 @@ import WalletCore import XCTest -class ElrondTests: XCTestCase { +class MultiversXTests: XCTestCase { let aliceBech32 = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" let alicePubKeyHex = "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1" @@ -17,8 +17,8 @@ class ElrondTests: XCTestCase { func testAddress() { let key = PrivateKey(data: Data(hexString: aliceSeedHex)!)! let pubkey = key.getPublicKeyEd25519() - let address = AnyAddress(publicKey: pubkey, coin: .elrond) - let addressFromString = AnyAddress(string: aliceBech32, coin: .elrond)! + let address = AnyAddress(publicKey: pubkey, coin: .multiversX) + let addressFromString = AnyAddress(string: aliceBech32, coin: .multiversX)! XCTAssertEqual(pubkey.data.hexString, alicePubKeyHex) XCTAssertEqual(address.description, addressFromString.description) @@ -27,9 +27,9 @@ class ElrondTests: XCTestCase { func testSignGenericAction() { let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! - let input = ElrondSigningInput.with { - $0.genericAction = ElrondGenericAction.with { - $0.accounts = ElrondAccounts.with { + let input = MultiversXSigningInput.with { + $0.genericAction = MultiversXGenericAction.with { + $0.accounts = MultiversXAccounts.with { $0.senderNonce = 7 $0.sender = aliceBech32 $0.receiver = bobBech32 @@ -44,7 +44,7 @@ class ElrondTests: XCTestCase { $0.privateKey = privateKey.data } - let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let output: MultiversXSigningOutput = AnySigner.sign(input: input, coin: .multiversX) let expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700" let expectedEncoded = #"{"nonce":7,"value":"0","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# @@ -56,9 +56,9 @@ class ElrondTests: XCTestCase { // Successfully broadcasted https://explorer.multiversx.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! - let input = ElrondSigningInput.with { - $0.genericAction = ElrondGenericAction.with { - $0.accounts = ElrondAccounts.with { + let input = MultiversXSigningInput.with { + $0.genericAction = MultiversXGenericAction.with { + $0.accounts = MultiversXAccounts.with { $0.senderNonce = 6 $0.sender = "erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa" $0.receiver = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r" @@ -73,7 +73,7 @@ class ElrondTests: XCTestCase { $0.privateKey = privateKey.data } - let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let output: MultiversXSigningOutput = AnySigner.sign(input: input, coin: .multiversX) let expectedSignature = "89f9683af92f7b835bff4e1d0dbfcff5245b3367df4d23538eb799e0ad0a90be29ac3bd3598ce55b35b35ebef68bfa5738eed39fd01adc33476f65bd1b966e0b" let expectedEncoded = #"{"nonce":6,"value":"0","receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r","sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa","gasPrice":1000000000,"gasLimit":12000000,"data":"dW5EZWxlZ2F0ZUAwZGUwYjZiM2E3NjQwMDAw","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# @@ -85,9 +85,9 @@ class ElrondTests: XCTestCase { // Successfully broadcasted https://explorer.multiversx.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! - let input = ElrondSigningInput.with { - $0.genericAction = ElrondGenericAction.with { - $0.accounts = ElrondAccounts.with { + let input = MultiversXSigningInput.with { + $0.genericAction = MultiversXGenericAction.with { + $0.accounts = MultiversXAccounts.with { $0.senderNonce = 1 $0.sender = "erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa" $0.receiver = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r" @@ -102,7 +102,7 @@ class ElrondTests: XCTestCase { $0.privateKey = privateKey.data } - let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let output: MultiversXSigningOutput = AnySigner.sign(input: input, coin: .multiversX) let expectedSignature = "3b9164d47a4e3c0330ae387cd29ba6391f9295acf5e43a16a4a2611645e66e5fa46bf22294ca68fe1948adf45cec8cb47b8792afcdb248bd9adec7c6e6c27108" let expectedEncoded = #"{"nonce":1,"value":"1","receiver":"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r","sender":"erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa","gasPrice":1000000000,"gasLimit":12000000,"data":"ZGVsZWdhdGU=","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# @@ -113,9 +113,9 @@ class ElrondTests: XCTestCase { func testSignEGLDTransfer() { let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! - let input = ElrondSigningInput.with { - $0.egldTransfer = ElrondEGLDTransfer.with { - $0.accounts = ElrondAccounts.with { + let input = MultiversXSigningInput.with { + $0.egldTransfer = MultiversXEGLDTransfer.with { + $0.accounts = MultiversXAccounts.with { $0.senderNonce = 7 $0.sender = aliceBech32 $0.receiver = bobBech32 @@ -126,7 +126,7 @@ class ElrondTests: XCTestCase { $0.privateKey = privateKey.data } - let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let output: MultiversXSigningOutput = AnySigner.sign(input: input, coin: .multiversX) let expectedSignature = "7e1c4c63b88ea72dcf7855a54463b1a424eb357ac3feb4345221e512ce07c7a50afb6d7aec6f480b554e32cf2037082f3bc17263d1394af1f3ef240be53c930b" let expectedEncoded = #"{"nonce":7,"value":"1000000000000000000","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# @@ -137,9 +137,9 @@ class ElrondTests: XCTestCase { func testSignESDTTransfer() { let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! - let input = ElrondSigningInput.with { - $0.esdtTransfer = ElrondESDTTransfer.with { - $0.accounts = ElrondAccounts.with { + let input = MultiversXSigningInput.with { + $0.esdtTransfer = MultiversXESDTTransfer.with { + $0.accounts = MultiversXAccounts.with { $0.senderNonce = 7 $0.sender = aliceBech32 $0.receiver = bobBech32 @@ -151,7 +151,7 @@ class ElrondTests: XCTestCase { $0.privateKey = privateKey.data } - let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let output: MultiversXSigningOutput = AnySigner.sign(input: input, coin: .multiversX) let expectedSignature = "9add6d9ac3f1a1fddb07b934e8a73cad3b8c232bdf29d723c1b38ad619905f03e864299d06eb3fe3bbb48a9f1d9b7f14e21dc5eaffe0c87f5718ad0c4198bb0c" let expectedData = "RVNEVFRyYW5zZmVyQDRkNTk1NDRmNGI0NTRlMmQzMTMyMzMzNEAwOTE4NGU3MmEwMDA=" let expectedEncoded = #"{"nonce":7,"value":"0","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":425000,"data":"\#(expectedData)","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# @@ -163,9 +163,9 @@ class ElrondTests: XCTestCase { func testSignESDTNFTTransfer() { let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! - let input = ElrondSigningInput.with { - $0.esdtnftTransfer = ElrondESDTNFTTransfer.with { - $0.accounts = ElrondAccounts.with { + let input = MultiversXSigningInput.with { + $0.esdtnftTransfer = MultiversXESDTNFTTransfer.with { + $0.accounts = MultiversXAccounts.with { $0.senderNonce = 7 $0.sender = aliceBech32 $0.receiver = bobBech32 @@ -178,7 +178,7 @@ class ElrondTests: XCTestCase { $0.privateKey = privateKey.data } - let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let output: MultiversXSigningOutput = AnySigner.sign(input: input, coin: .multiversX) let expectedSignature = "cc935685d5b31525e059a16a832cba98dee751983a5a93de4198f6553a2c55f5f1e0b4300fe9077376fa754546da0b0f6697e66462101a209aafd0fc775ab60a" let expectedData = "RVNEVE5GVFRyYW5zZmVyQDRjNGI0ZDQ1NTgyZDYxNjE2MjM5MzEzMEAwNEAwMjhlYzNkZmEwMWFjMDAwQDgwNDlkNjM5ZTVhNjk4MGQxY2QyMzkyYWJjY2U0MTAyOWNkYTc0YTE1NjM1MjNhMjAyZjA5NjQxY2MyNjE4Zjg=" let expectedEncoded = #"{"nonce":7,"value":"0","receiver":"\#(aliceBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":937500,"data":"\#(expectedData)","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index eced318a140..d641e7150d6 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -64,7 +64,7 @@ class CoinAddressDerivationTests: XCTestCase { case .dogecoin: let expectedResult = "DJRFZNg8jkUtjcpo2zJd92FUAzwRjitw6f" assertCoinDerivation(coin, expectedResult, derivedAddress, address) - case .elrond: + case .multiversX: let expectedResult = "erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .eos, .wax: diff --git a/tests/chains/Elrond/AddressTests.cpp b/tests/chains/MultiversX/AddressTests.cpp similarity index 86% rename from tests/chains/Elrond/AddressTests.cpp rename to tests/chains/MultiversX/AddressTests.cpp index a96a0f53c1a..01d820489d1 100644 --- a/tests/chains/Elrond/AddressTests.cpp +++ b/tests/chains/MultiversX/AddressTests.cpp @@ -7,7 +7,7 @@ #include #include -#include "Elrond/Address.h" +#include "MultiversX/Address.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" @@ -15,14 +15,14 @@ using namespace TW; -namespace TW::Elrond::tests { +namespace TW::MultiversX::tests { -TEST(ElrondAddress, Valid) { +TEST(MultiversXAddress, Valid) { ASSERT_TRUE(Address::isValid(ALICE_BECH32)); ASSERT_TRUE(Address::isValid(BOB_BECH32)); } -TEST(ElrondAddress, Invalid) { +TEST(MultiversXAddress, Invalid) { ASSERT_FALSE(Address::isValid("")); ASSERT_FALSE(Address::isValid("foo")); ASSERT_FALSE(Address::isValid("10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); @@ -31,7 +31,7 @@ TEST(ElrondAddress, Invalid) { ASSERT_FALSE(Address::isValid(ALICE_PUBKEY_HEX)); } -TEST(ElrondAddress, FromString) { +TEST(MultiversXAddress, FromString) { Address alice, bob, carol; ASSERT_TRUE(Address::decode(ALICE_BECH32, alice)); ASSERT_TRUE(Address::decode(BOB_BECH32, bob)); @@ -40,7 +40,7 @@ TEST(ElrondAddress, FromString) { ASSERT_EQ(BOB_PUBKEY_HEX, hex(bob.getKeyHash())); } -TEST(ElrondAddress, FromData) { +TEST(MultiversXAddress, FromData) { const auto alice = Address(parse_hex(ALICE_PUBKEY_HEX)); const auto bob = Address(parse_hex(BOB_PUBKEY_HEX)); @@ -48,7 +48,7 @@ TEST(ElrondAddress, FromData) { ASSERT_EQ(BOB_BECH32, bob.string()); } -TEST(ElrondAddress, FromPrivateKey) { +TEST(MultiversXAddress, FromPrivateKey) { auto aliceKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); auto alice = Address(aliceKey.getPublicKey(TWPublicKeyTypeED25519)); ASSERT_EQ(ALICE_BECH32, alice.string()); @@ -58,7 +58,7 @@ TEST(ElrondAddress, FromPrivateKey) { ASSERT_EQ(BOB_BECH32, bob.string()); } -TEST(ElrondAddress, FromPublicKey) { +TEST(MultiversXAddress, FromPublicKey) { auto alice = PublicKey(parse_hex(ALICE_PUBKEY_HEX), TWPublicKeyTypeED25519); ASSERT_EQ(ALICE_BECH32, Address(alice).string()); @@ -66,4 +66,4 @@ TEST(ElrondAddress, FromPublicKey) { ASSERT_EQ(BOB_BECH32, Address(bob).string()); } -} // namespace TW::Elrond::tests +} // namespace TW::MultiversX::tests diff --git a/tests/chains/Elrond/SerializationTests.cpp b/tests/chains/MultiversX/SerializationTests.cpp similarity index 87% rename from tests/chains/Elrond/SerializationTests.cpp rename to tests/chains/MultiversX/SerializationTests.cpp index c1a730de8c7..e1fe9e875e4 100644 --- a/tests/chains/Elrond/SerializationTests.cpp +++ b/tests/chains/MultiversX/SerializationTests.cpp @@ -8,15 +8,15 @@ #include #include -#include "Elrond/Serialization.h" +#include "MultiversX/Serialization.h" #include "HexCoding.h" #include "TestAccounts.h" using namespace TW; -namespace TW::Elrond::tests { +namespace TW::MultiversX::tests { -TEST(ElrondSerialization, SignableString) { +TEST(MultiversXSerialization, SignableString) { Transaction transaction; transaction.nonce = 42; transaction.value = "43"; @@ -30,7 +30,7 @@ TEST(ElrondSerialization, SignableString) { ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"bob","sender":"alice","gasPrice":0,"gasLimit":0,"data":"Zm9v","chainID":"1","version":1})", jsonString); } -TEST(ElrondSerialization, SignableStringWithRealData) { +TEST(MultiversXSerialization, SignableStringWithRealData) { Transaction transaction; transaction.nonce = 15; transaction.value = "100"; @@ -47,7 +47,7 @@ TEST(ElrondSerialization, SignableStringWithRealData) { ASSERT_EQ(expected, actual); } -TEST(ElrondSerialization, SignableStringWithoutData) { +TEST(MultiversXSerialization, SignableStringWithoutData) { Transaction transaction; transaction.nonce = 42; transaction.value = "43"; @@ -60,4 +60,4 @@ TEST(ElrondSerialization, SignableStringWithoutData) { ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"abba","sender":"feed","gasPrice":0,"gasLimit":0,"chainID":"1","version":1})", jsonString); } -} // namespace TW::Elrond::tests +} // namespace TW::MultiversX::tests diff --git a/tests/chains/Elrond/SignerTests.cpp b/tests/chains/MultiversX/SignerTests.cpp similarity index 95% rename from tests/chains/Elrond/SignerTests.cpp rename to tests/chains/MultiversX/SignerTests.cpp index 12172fa73d8..a9da1cd485a 100644 --- a/tests/chains/Elrond/SignerTests.cpp +++ b/tests/chains/MultiversX/SignerTests.cpp @@ -8,9 +8,9 @@ #include #include -#include "Elrond/Address.h" -#include "Elrond/Signer.h" -#include "Elrond/Codec.h" +#include "MultiversX/Address.h" +#include "MultiversX/Codec.h" +#include "MultiversX/Signer.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" @@ -19,9 +19,9 @@ using namespace TW; -namespace TW::Elrond::tests { +namespace TW::MultiversX::tests { -TEST(ElrondSigner, SignGenericAction) { +TEST(MultiversXSigner, SignGenericAction) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -46,7 +46,7 @@ TEST(ElrondSigner, SignGenericAction) { ASSERT_EQ(expectedSignature, signature); } -TEST(ElrondSigner, SignGenericActionUnDelegate) { +TEST(MultiversXSigner, SignGenericActionUnDelegate) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -55,7 +55,7 @@ TEST(ElrondSigner, SignGenericActionUnDelegate) { input.mutable_generic_action()->mutable_accounts()->set_sender("erd1aajqh5xjka5fk0c235dwy7qd6lkz2e29tlhy8gncuq0mcr68q34qgswnqa"); input.mutable_generic_action()->mutable_accounts()->set_receiver("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhllllscrt56r"); input.mutable_generic_action()->set_value("0"); - input.mutable_generic_action()->set_data("unDelegate@" + TW::Elrond::Codec::encodeBigInt("1000000000000000000")); + input.mutable_generic_action()->set_data("unDelegate@" + TW::MultiversX::Codec::encodeBigInt("1000000000000000000")); input.mutable_generic_action()->set_version(1); input.set_gas_price(1000000000); input.set_gas_limit(12000000); @@ -83,7 +83,7 @@ TEST(ElrondSigner, SignGenericActionUnDelegate) { // Successfully broadcasted https://explorer.multiversx.com/transactions/3301ae5a6a77f0ab9ceb5125258f12539a113b0c6787de76a5c5867f2c515d65 } -TEST(ElrondSigner, SignGenericActionRedelegateRewards) { +TEST(MultiversXSigner, SignGenericActionRedelegateRewards) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -119,7 +119,7 @@ TEST(ElrondSigner, SignGenericActionRedelegateRewards) { ASSERT_EQ(expectedSignature, signature); } -TEST(ElrondSigner, SignGenericActionClaimRewards) { +TEST(MultiversXSigner, SignGenericActionClaimRewards) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -155,7 +155,7 @@ TEST(ElrondSigner, SignGenericActionClaimRewards) { ASSERT_EQ(expectedSignature, signature); } -TEST(ElrondSigner, SignGenericActionWithdrawStake) { +TEST(MultiversXSigner, SignGenericActionWithdrawStake) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -192,7 +192,7 @@ TEST(ElrondSigner, SignGenericActionWithdrawStake) { // Need to wait 9 days for broadcasting } -TEST(ElrondSigner, SignGenericActionDelegate) { +TEST(MultiversXSigner, SignGenericActionDelegate) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -229,7 +229,7 @@ TEST(ElrondSigner, SignGenericActionDelegate) { // Successfully broadcasted https://explorer.multiversx.com/transactions/e5007662780f8ed677b37b156007c24bf60b7366000f66ec3525cfa16a4564e7 } -TEST(ElrondSigner, SignGenericActionJSON) { +TEST(MultiversXSigner, SignGenericActionJSON) { // Shuffle some fields, assume arbitrary order in the input auto input = (boost::format(R"({"genericAction" : {"accounts": {"senderNonce": 7, "receiver": "%1%", "sender": "%2%"}, "data": "foo", "value": "0", "version": 1}, "gasPrice": 1000000000, "gasLimit": 50000, "chainId": "1"})") % BOB_BECH32 % ALICE_BECH32).str(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); @@ -241,7 +241,7 @@ TEST(ElrondSigner, SignGenericActionJSON) { ASSERT_EQ(expectedEncoded, encoded); } -TEST(ElrondSigner, SignWithoutData) { +TEST(MultiversXSigner, SignWithoutData) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -266,7 +266,7 @@ TEST(ElrondSigner, SignWithoutData) { ASSERT_EQ(expectedEncoded, encoded); } -TEST(ElrondSigner, SignJSONWithoutData) { +TEST(MultiversXSigner, SignJSONWithoutData) { // Shuffle some fields, assume arbitrary order in the input auto input = (boost::format(R"({"genericAction" : {"accounts": {"senderNonce": 0, "receiver": "%1%", "sender": "%2%"}, "value": "0", "version": 1}, "gasPrice": 1000000000, "gasLimit": 50000, "chainId": "1"})") % BOB_BECH32 % ALICE_BECH32).str(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); @@ -278,8 +278,8 @@ TEST(ElrondSigner, SignJSONWithoutData) { ASSERT_EQ(expectedEncoded, encoded); } -TEST(ElrondSigner, SignWithUsernames) { - // https://github.com/ElrondNetwork/elrond-go/blob/master/examples/construction_test.go, scenario "TestConstructTransaction_Usernames". +TEST(MultiversXSigner, SignWithUsernames) { + // https://github.com/multiversx/mx-chain-go/blob/master/examples/construction_test.go, scenario "TestConstructTransaction_Usernames". auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); @@ -313,7 +313,7 @@ TEST(ElrondSigner, SignWithUsernames) { ASSERT_EQ(expectedEncoded, encoded); } -TEST(ElrondSigner, SignWithOptions) { +TEST(MultiversXSigner, SignWithOptions) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -327,7 +327,6 @@ TEST(ElrondSigner, SignWithOptions) { // We'll set a dummy value on the "options" field (merely an example). // Currently, the "options" field should be ignored (not set) by applications using TW Core. // In the future, TW Core will handle specific transaction options - // (such as the "SignedWithHash" flag, as seen in https://github.com/ElrondNetwork/elrond-go-core/blob/main/core/versioning/txVersionChecker.go) // when building and signing transactions. input.mutable_generic_action()->set_options(42); input.set_gas_price(1000000000); @@ -345,7 +344,7 @@ TEST(ElrondSigner, SignWithOptions) { ASSERT_EQ(expectedSignature, signature); } -TEST(ElrondSigner, SignEGLDTransfer) { +TEST(MultiversXSigner, SignEGLDTransfer) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -366,7 +365,7 @@ TEST(ElrondSigner, SignEGLDTransfer) { ASSERT_EQ(expectedEncoded, encoded); } -TEST(ElrondSigner, SignESDTTransfer) { +TEST(MultiversXSigner, SignESDTTransfer) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -391,7 +390,7 @@ TEST(ElrondSigner, SignESDTTransfer) { ASSERT_EQ(expectedEncoded, encoded); } -TEST(ElrondSigner, SignESDTNFTTransfer) { +TEST(MultiversXSigner, SignESDTNFTTransfer) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -417,4 +416,4 @@ TEST(ElrondSigner, SignESDTNFTTransfer) { ASSERT_EQ(expectedEncoded, encoded); } -} // namespace TW::Elrond::tests +} // namespace TW::MultiversX::tests diff --git a/tests/chains/Elrond/TWAnySignerTests.cpp b/tests/chains/MultiversX/TWAnySignerTests.cpp similarity index 89% rename from tests/chains/Elrond/TWAnySignerTests.cpp rename to tests/chains/MultiversX/TWAnySignerTests.cpp index 09785661191..91a6315b550 100644 --- a/tests/chains/Elrond/TWAnySignerTests.cpp +++ b/tests/chains/MultiversX/TWAnySignerTests.cpp @@ -7,7 +7,7 @@ #include "boost/format.hpp" #include -#include "Elrond/Signer.h" +#include "MultiversX/Signer.h" #include "HexCoding.h" #include "TestAccounts.h" #include "TestUtilities.h" @@ -15,9 +15,9 @@ using namespace TW; -namespace TW::Elrond::tests { +namespace TW::MultiversX::tests { -TEST(TWAnySignerElrond, Sign) { +TEST(TWAnySignerMultiversX, Sign) { auto input = Proto::SigningInput(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); @@ -33,7 +33,7 @@ TEST(TWAnySignerElrond, Sign) { input.set_chain_id("1"); Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeElrond); + ANY_SIGN(input, TWCoinTypeMultiversX); auto signature = output.signature(); auto encoded = output.encoded(); @@ -44,16 +44,16 @@ TEST(TWAnySignerElrond, Sign) { ASSERT_EQ(expectedEncoded, encoded); } -TEST(TWAnySignerElrond, SignJSON) { +TEST(TWAnySignerMultiversX, SignJSON) { // Shuffle some fields, assume arbitrary order in the input auto input = STRING((boost::format(R"({"genericAction" : {"accounts": {"senderNonce": 7, "receiver": "%1%", "sender": "%2%"}, "data": "foo", "value": "0", "version": 1}, "gasPrice": 1000000000, "gasLimit": 50000, "chainId": "1"})") % BOB_BECH32 % ALICE_BECH32).str().c_str()); auto privateKey = DATA(ALICE_SEED_HEX); - auto encoded = WRAPS(TWAnySignerSignJSON(input.get(), privateKey.get(), TWCoinTypeElrond)); + auto encoded = WRAPS(TWAnySignerSignJSON(input.get(), privateKey.get(), TWCoinTypeMultiversX)); auto expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700"; auto expectedEncoded = (boost::format(R"({"nonce":7,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeElrond)); + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeMultiversX)); assertStringsEqual(encoded, expectedEncoded.c_str()); } -} // namespace TW::Elrond::tests +} // namespace TW::MultiversX::tests diff --git a/tests/chains/Elrond/TWCoinTypeTests.cpp b/tests/chains/MultiversX/TWCoinTypeTests.cpp similarity index 69% rename from tests/chains/Elrond/TWCoinTypeTests.cpp rename to tests/chains/MultiversX/TWCoinTypeTests.cpp index 5502507c216..2a08eeb0574 100644 --- a/tests/chains/Elrond/TWCoinTypeTests.cpp +++ b/tests/chains/MultiversX/TWCoinTypeTests.cpp @@ -13,19 +13,19 @@ #include -TEST(TWElrondCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeElrond)); +TEST(TWMultiversXCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeMultiversX)); auto txId = WRAPS(TWStringCreateWithUTF8Bytes("163b46551a74626415074b626d2f37d3c78aef0f6ccb628db434ee65a35ea127")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeElrond, txId.get())); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeMultiversX, txId.get())); auto accId = WRAPS(TWStringCreateWithUTF8Bytes("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeElrond, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeElrond)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeElrond)); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeMultiversX, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeMultiversX)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeMultiversX)); - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeElrond), 18); - ASSERT_EQ(TWBlockchainElrondNetwork, TWCoinTypeBlockchain(TWCoinTypeElrond)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeElrond)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeElrond)); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeMultiversX), 18); + ASSERT_EQ(TWBlockchainMultiversX, TWCoinTypeBlockchain(TWCoinTypeMultiversX)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeMultiversX)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeMultiversX)); assertStringsEqual(symbol, "eGLD"); assertStringsEqual(txUrl, "https://explorer.multiversx.com/transactions/163b46551a74626415074b626d2f37d3c78aef0f6ccb628db434ee65a35ea127"); assertStringsEqual(accUrl, "https://explorer.multiversx.com/accounts/erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); diff --git a/tests/chains/Elrond/TestAccounts.h b/tests/chains/MultiversX/TestAccounts.h similarity index 90% rename from tests/chains/Elrond/TestAccounts.h rename to tests/chains/MultiversX/TestAccounts.h index 80e45e638c3..07bfa1aaa95 100644 --- a/tests/chains/Elrond/TestAccounts.h +++ b/tests/chains/MultiversX/TestAccounts.h @@ -7,7 +7,7 @@ #pragma once // Well-known accounts on Testnet & Devnet, -// https://github.com/ElrondNetwork/elrond-sdk-erdpy/tree/main/erdpy/testnet/wallets/users: +// https://github.com/multiversx/mx-sdk-testwallets: const auto ALICE_BECH32 = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"; const auto ALICE_PUBKEY_HEX = "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"; const auto ALICE_SEED_HEX = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; diff --git a/tests/chains/Elrond/TransactionFactoryTests.cpp b/tests/chains/MultiversX/TransactionFactoryTests.cpp similarity index 94% rename from tests/chains/Elrond/TransactionFactoryTests.cpp rename to tests/chains/MultiversX/TransactionFactoryTests.cpp index 8837d2cab6b..cb8418f5624 100644 --- a/tests/chains/Elrond/TransactionFactoryTests.cpp +++ b/tests/chains/MultiversX/TransactionFactoryTests.cpp @@ -7,12 +7,12 @@ #include #include -#include "Elrond/TransactionFactory.h" +#include "MultiversX/TransactionFactory.h" #include "TestAccounts.h" -namespace TW::Elrond::tests { +namespace TW::MultiversX::tests { -TEST(ElrondTransactionFactory, fromEGLDTransfer) { +TEST(MultiversXTransactionFactory, fromEGLDTransfer) { auto input = Proto::SigningInput(); input.mutable_egld_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); input.mutable_egld_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); @@ -31,7 +31,7 @@ TEST(ElrondTransactionFactory, fromEGLDTransfer) { ASSERT_EQ(1ul, transaction.version); } -TEST(ElrondTransactionFactory, fromESDTTransfer) { +TEST(MultiversXTransactionFactory, fromESDTTransfer) { auto input = Proto::SigningInput(); input.mutable_esdt_transfer()->mutable_accounts()->set_sender_nonce(7); input.mutable_esdt_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); @@ -52,7 +52,7 @@ TEST(ElrondTransactionFactory, fromESDTTransfer) { ASSERT_EQ(1ul, transaction.version); } -TEST(ElrondTransactionFactory, fromESDTNFTTransfer) { +TEST(MultiversXTransactionFactory, fromESDTNFTTransfer) { auto input = Proto::SigningInput(); input.mutable_esdtnft_transfer()->mutable_accounts()->set_sender_nonce(7); input.mutable_esdtnft_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); @@ -74,7 +74,7 @@ TEST(ElrondTransactionFactory, fromESDTNFTTransfer) { ASSERT_EQ(1ul, transaction.version); } -TEST(ElrondTransactionFactory, createTransfersWithProvidedNetworkConfig) { +TEST(MultiversXTransactionFactory, createTransfersWithProvidedNetworkConfig) { NetworkConfig networkConfig; // Set dummy values: @@ -121,7 +121,7 @@ TEST(ElrondTransactionFactory, createTransfersWithProvidedNetworkConfig) { ASSERT_EQ("T", tx3.chainID); } -TEST(ElrondTransactionFactory, createTransfersWithOverriddenNetworkParameters) { +TEST(MultiversXTransactionFactory, createTransfersWithOverriddenNetworkParameters) { Proto::SigningInput signingInputWithEGLDTransfer; signingInputWithEGLDTransfer.set_gas_limit(50500); signingInputWithEGLDTransfer.set_gas_price(1000000001); @@ -155,7 +155,7 @@ TEST(ElrondTransactionFactory, createTransfersWithOverriddenNetworkParameters) { ASSERT_EQ("C", tx3.chainID); } -TEST(ElrondTransactionFactory, create) { +TEST(MultiversXTransactionFactory, create) { Proto::SigningInput signingInputWithGenericAction; signingInputWithGenericAction.mutable_generic_action()->set_data("hello"); @@ -189,4 +189,4 @@ TEST(ElrondTransactionFactory, create) { ASSERT_EQ("ESDTNFTTransfer@4c4b4d45582d616162393130@04@028ec3dfa01ac000@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", tx4.data); } -} // namespace TW::Elrond::tests +} // namespace TW::MultiversX::tests diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 7f54228c9a1..3a506f452fd 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -148,7 +148,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeWAX: EXPECT_EQ(address, "EOS5TrYnZP1RkDSUMzBY4GanCy6AP68kCMdkAb5EACkAwkdgRLShz"); break; - case TWCoinTypeElrond: + case TWCoinTypeMultiversX: EXPECT_EQ(address, "erd1a6f6fan035ttsxdmn04ellxdlnwpgyhg0lhx5vjv92v6rc8xw9yq83344f"); break; case TWCoinTypeEverscale: diff --git a/tests/common/CoinAddressValidationTests.cpp b/tests/common/CoinAddressValidationTests.cpp index 5f899c5c9f5..24912e02e3e 100644 --- a/tests/common/CoinAddressValidationTests.cpp +++ b/tests/common/CoinAddressValidationTests.cpp @@ -374,9 +374,9 @@ TEST(Coin, ValidateAddressVeChain) { EXPECT_EQ(normalizeAddress(TWCoinTypeVeChain, "0x9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f"), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); } -TEST(Coin, ValidateAddressElrond) { - EXPECT_TRUE(validateAddress(TWCoinTypeElrond, "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz")); - EXPECT_FALSE(validateAddress(TWCoinTypeElrond, "xerd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz")); +TEST(Coin, ValidateAddressMultiversX) { + EXPECT_TRUE(validateAddress(TWCoinTypeMultiversX, "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz")); + EXPECT_FALSE(validateAddress(TWCoinTypeMultiversX, "xerd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz")); } TEST(Coin, ValidateAddressOasis) { diff --git a/tests/interface/TWAnyAddressTests.cpp b/tests/interface/TWAnyAddressTests.cpp index f0384bd4c09..8096cd87182 100644 --- a/tests/interface/TWAnyAddressTests.cpp +++ b/tests/interface/TWAnyAddressTests.cpp @@ -141,10 +141,10 @@ TEST(TWAnyAddress, Data) { auto keyHash = WRAPD(TWAnyAddressData(addr.get())); assertHexEqual(keyHash, "172bdf43265c0adfe105a1a8c45b3f406a38362f24"); } - // elrond + // multiversx { auto string = STRING("erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"); - auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeElrond)); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeMultiversX)); auto pubkey = WRAPD(TWAnyAddressData(addr.get())); assertHexEqual(pubkey, "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293"); } diff --git a/tests/interface/TWCoinTypeTests.cpp b/tests/interface/TWCoinTypeTests.cpp index 5fd55733ef8..cb3ac95c1c9 100644 --- a/tests/interface/TWCoinTypeTests.cpp +++ b/tests/interface/TWCoinTypeTests.cpp @@ -28,7 +28,7 @@ TEST(TWCoinType, TWPurpose) { ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeBandChain)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeBluzelle)); ASSERT_EQ(TWPurposeBIP1852, TWCoinTypePurpose(TWCoinTypeCardano)); - ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeElrond)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeMultiversX)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeOasis)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTHORChain)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeCryptoOrg)); @@ -100,7 +100,7 @@ TEST(TWCoinType, TWPublicKeyType) { ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeBandChain)); ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeBluzelle)); ASSERT_EQ(TWPublicKeyTypeED25519Cardano, TWCoinTypePublicKeyType(TWCoinTypeCardano)); - ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeElrond)); + ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeMultiversX)); ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeOasis)); ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeTHORChain)); ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeCryptoOrg)); diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index a73b5f44bff..147ecad674e 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -286,11 +286,11 @@ TEST(HDWallet, DeriveAlgorand) { assertHexEqual(privateKeyData, "ce0b7ac644e2b7d9d14d3928b11643f43e48c33d3e328d059fef8add7f070e82"); } -TEST(HDWallet, DeriveElrond) { +TEST(HDWallet, DeriveMultiversX) { auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); - auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeElrond)); + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeMultiversX)); auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); - auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeElrond, privateKey.get())); + auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeMultiversX, privateKey.get())); assertHexEqual(privateKeyData, "0eb593141de897d60a0883320793eb49e63d556ccdf783a87ec014f150d50453"); assertStringsEqual(address, "erd1a6l7q9cfvrgr80xuzm37tapdr4zm3mwrtl6vt8f45df45x7eadfs8ds5vv"); diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index 22195dcd3e0..7946c79c9e5 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -30,7 +30,7 @@ TEST(TWHRP, StringForHRP) { ASSERT_STREQ(stringForHRP(TWHRPBandChain), "band"); ASSERT_STREQ(stringForHRP(TWHRPBluzelle), "bluzelle"); ASSERT_STREQ(stringForHRP(TWHRPCardano), "addr"); - ASSERT_STREQ(stringForHRP(TWHRPElrond), "erd"); + ASSERT_STREQ(stringForHRP(TWHRPMultiversX), "erd"); ASSERT_STREQ(stringForHRP(TWHRPOasis), "oasis"); ASSERT_STREQ(stringForHRP(TWHRPTHORChain), "thor"); ASSERT_STREQ(stringForHRP(TWHRPCryptoOrg), "cro"); @@ -56,7 +56,7 @@ TEST(TWHRP, HRPForString) { ASSERT_EQ(hrpForString("kava"), TWHRPKava); ASSERT_EQ(hrpForString("band"), TWHRPBandChain); ASSERT_EQ(hrpForString("addr"), TWHRPCardano); - ASSERT_EQ(hrpForString("erd"), TWHRPElrond); + ASSERT_EQ(hrpForString("erd"), TWHRPMultiversX); ASSERT_EQ(hrpForString("oasis"), TWHRPOasis); ASSERT_EQ(hrpForString("thor"), TWHRPTHORChain); ASSERT_EQ(hrpForString("bluzelle"), TWHRPBluzelle); @@ -84,7 +84,7 @@ TEST(TWHPR, HPRByCoinType) { ASSERT_EQ(TWHRPBandChain, TWCoinTypeHRP(TWCoinTypeBandChain)); ASSERT_EQ(TWHRPBluzelle, TWCoinTypeHRP(TWCoinTypeBluzelle)); ASSERT_EQ(TWHRPCardano, TWCoinTypeHRP(TWCoinTypeCardano)); - ASSERT_EQ(TWHRPElrond, TWCoinTypeHRP(TWCoinTypeElrond)); + ASSERT_EQ(TWHRPMultiversX, TWCoinTypeHRP(TWCoinTypeMultiversX)); ASSERT_EQ(TWHRPOasis, TWCoinTypeHRP(TWCoinTypeOasis)); ASSERT_EQ(TWHRPTHORChain, TWCoinTypeHRP(TWCoinTypeTHORChain)); ASSERT_EQ(TWHRPCryptoOrg, TWCoinTypeHRP(TWCoinTypeCryptoOrg)); From df8d72046a989f3b2f1c25013d94262e1737a936 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 3 Feb 2023 11:58:04 +0100 Subject: [PATCH 089/426] feat(codegen): mark java class as final to avoid kt doc confusion (#2881) --- codegen/lib/templates/java/class.erb | 4 ++-- codegen/lib/templates/java/struct.erb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codegen/lib/templates/java/class.erb b/codegen/lib/templates/java/class.erb index 69cd027f403..fa491ff2560 100644 --- a/codegen/lib/templates/java/class.erb +++ b/codegen/lib/templates/java/class.erb @@ -5,9 +5,9 @@ import java.util.HashSet; <% equal = entity.static_methods.detect{ |i| i.name == 'Equal' } -%> <%= entity.comment %> <% if !less.nil? && !equal.nil? -%> -public class <%= entity.name %> implements Comparable<<%= entity.name %>> { +public final class <%= entity.name %> implements Comparable<<%= entity.name %>> { <% else -%> -public class <%= entity.name %> { +public final class <%= entity.name %> { <% end -%> private long nativeHandle; diff --git a/codegen/lib/templates/java/struct.erb b/codegen/lib/templates/java/struct.erb index bb57cf25017..6f09dda87d2 100644 --- a/codegen/lib/templates/java/struct.erb +++ b/codegen/lib/templates/java/struct.erb @@ -4,9 +4,9 @@ import java.security.InvalidParameterException; <% equal = entity.static_methods.detect{ |i| i.name == 'Equal' } -%> <%= entity.comment %> <% if !less.nil? && !equal.nil? -%> -public class <%= entity.name %> implements Comparable<<%= entity.name %>> { +public final class <%= entity.name %> implements Comparable<<%= entity.name %>> { <% else -%> -public class <%= entity.name %> { +public final class <%= entity.name %> { <% end -%> private byte[] bytes; From c2dae8abc67d7385a595d484b39193b7805242e1 Mon Sep 17 00:00:00 2001 From: hewigovens <360470+hewigovens@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:59:52 +0900 Subject: [PATCH 090/426] Fix empty lock script data for Flux (zelcash) (#2905) --- .github/workflows/linux-ci-sonarcloud.yml | 4 ++-- src/Bitcoin/Script.cpp | 1 + swift/Tests/Blockchains/ZcashTests.swift | 8 ++++++++ tools/install-rust-dependencies | 6 +++--- tools/ios-build | 4 ++-- tools/rust-bindgen | 4 +++- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/linux-ci-sonarcloud.yml b/.github/workflows/linux-ci-sonarcloud.yml index 662c53dde90..8941656e9b5 100644 --- a/.github/workflows/linux-ci-sonarcloud.yml +++ b/.github/workflows/linux-ci-sonarcloud.yml @@ -14,8 +14,8 @@ jobs: - uses: actions/checkout@v2 - name: Install system dependencies run: | - # build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake - sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev --fix-missing + tools/install-sys-dependencies-linux + tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache uses: actions/cache@v1.1.2 diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index 407a8e5152e..08bbaa042f3 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -376,6 +376,7 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c return {}; case TWCoinTypeZcash: + case TWCoinTypeZelcash: if (Zcash::TAddress::isValid(string)) { auto address = Zcash::TAddress(string); auto data = Data(); diff --git a/swift/Tests/Blockchains/ZcashTests.swift b/swift/Tests/Blockchains/ZcashTests.swift index bd7a861a0f4..92229c34186 100644 --- a/swift/Tests/Blockchains/ZcashTests.swift +++ b/swift/Tests/Blockchains/ZcashTests.swift @@ -74,4 +74,12 @@ class ZcashTests: XCTestCase { XCTAssertEqual(output.error, TW_Common_Proto_SigningError.ok) XCTAssertEqual(output.encoded.hexString, "0400008085202f890153685b8809efc50dd7d5cb0906b307a1b8aa5157baa5fc1bd6fe2d0344dd193a000000006b483045022100ca0be9f37a4975432a52bb65b25e483f6f93d577955290bb7fb0060a93bfc92002203e0627dff004d3c72a957dc9f8e4e0e696e69d125e4d8e275d119001924d3b48012103b243171fae5516d1dc15f9178cfcc5fdc67b0a883055c117b01ba8af29b953f6ffffffff0140720700000000001976a91449964a736f3713d64283fd0018626ba50091c7e988ac00000000000000000000000000000000000000") } + + func testLockScript() { + let script = BitcoinScript.lockScriptForAddress(address: "t1NsqaL1G2XD6xWfVmeAi9gLUVFct59zJu4", coin: .zcash) + XCTAssertTrue(!script.data.isEmpty) + + let script2 = BitcoinScript.lockScriptForAddress(address: "t1XeXHdaaXbdEaBCz1cMib5rvg34RjTWR6N", coin: .zelcash) + XCTAssertTrue(!script2.data.isEmpty) + } } diff --git a/tools/install-rust-dependencies b/tools/install-rust-dependencies index b2356e561d6..911065f4ce2 100755 --- a/tools/install-rust-dependencies +++ b/tools/install-rust-dependencies @@ -6,8 +6,8 @@ if [[ `uname` == "Darwin" ]]; then rustup update rustup toolchain install nightly rustup default nightly - rustup toolchain install nightly-x86_64-apple-darwin - rustup toolchain install nightly-aarch64-apple-darwin + rustup toolchain install nightly-x86_64-apple-darwin --force-non-host + rustup toolchain install nightly-aarch64-apple-darwin --force-non-host rustup component add rust-src --toolchain nightly-aarch64-apple-darwin rustup component add rust-src --toolchain nightly-x86_64-apple-darwin fi @@ -17,7 +17,7 @@ rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-and # iOS rustup target add aarch64-apple-darwin x86_64-apple-darwin # macOS -rustup target add x86_64-apple-ios aarch64-apple-ios-sim aarch64-apple-ios +rustup target add x86_64-apple-ios aarch64-apple-ios-sim aarch64-apple-ios # Wasm rustup target add wasm32-unknown-emscripten diff --git a/tools/ios-build b/tools/ios-build index 8e12031289e..ace228be684 100755 --- a/tools/ios-build +++ b/tools/ios-build @@ -47,13 +47,13 @@ create_xc_framework() { xcodebuild -create-xcframework -output $BUILD_FOLDER/$FRAMEWORK.xcframework \ -framework $BUILD_FOLDER/ios-arm64.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ -framework $BUILD_FOLDER/ios-arm64_x86_64-simulator.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ - -framework $BUILD_FOLDER/ios-x86_64_arm64-maccatalyst.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ -framework $BUILD_FOLDER/macos-arm64_x86_64.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework } main() { init - build_mac_x64_arm64 && build_ios_mac_catalyst + build_mac_x64_arm64 + #build_ios_mac_catalyst build_ios_arm64 && build_ios_simulator create_xc_framework } diff --git a/tools/rust-bindgen b/tools/rust-bindgen index ab885868d1c..c866a43b2ee 100755 --- a/tools/rust-bindgen +++ b/tools/rust-bindgen @@ -1,5 +1,7 @@ #!/bin/bash +set -e + TARGET_NAME="libwallet_core_rs.a" TARGET_XCFRAMEWORK_NAME=../swift/WalletCoreRs.xcframework BUILD_FOLDER=../build/local @@ -8,7 +10,7 @@ HEADER_NAME="WalletCoreRSBindgen.h" create_xc_framework() { rm -rf $TARGET_XCFRAMEWORK_NAME - xcodebuild -create-xcframework -library $BUILD_FOLDER/$TARGET_NAME -library $BUILD_FOLDER/darwin_universal/$TARGET_NAME -library $BUILD_FOLDER/aarch64-apple-ios/release/$TARGET_NAME -library $BUILD_FOLDER/catalyst/$TARGET_NAME -output $TARGET_XCFRAMEWORK_NAME + xcodebuild -create-xcframework -library $BUILD_FOLDER/$TARGET_NAME -library $BUILD_FOLDER/darwin_universal/$TARGET_NAME -library $BUILD_FOLDER/aarch64-apple-ios/release/$TARGET_NAME -output $TARGET_XCFRAMEWORK_NAME } cd rust From a068cc9d45a3beaee9fdecb57261b22c87018796 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 3 Feb 2023 16:37:31 +0100 Subject: [PATCH 091/426] feat(ios-build): remove catalyst for fastlane (#2906) --- swift/common-xcframework.yml | 2 +- swift/project.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/common-xcframework.yml b/swift/common-xcframework.yml index dae0d4b55c7..d556db5dcdc 100644 --- a/swift/common-xcframework.yml +++ b/swift/common-xcframework.yml @@ -45,7 +45,7 @@ targets: - framework: WalletCoreRs.xcframework settings: SKIP_INSTALL: false - SUPPORTS_MACCATALYST: true + SUPPORTS_MACCATALYST: false INFOPLIST_FILE: 'Info.plist' CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION: YES_ERROR CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER: $(inherited) false diff --git a/swift/project.yml b/swift/project.yml index df46f6a29e5..69afe8dff90 100644 --- a/swift/project.yml +++ b/swift/project.yml @@ -45,7 +45,7 @@ targets: gatherCoverageData: true settings: SKIP_INSTALL: false - SUPPORTS_MACCATALYST: true + SUPPORTS_MACCATALYST: false DEBUG_INFORMATION_FORMAT: dwarf-with-dsym BUILD_LIBRARY_FOR_DISTRIBUTION: true INFOPLIST_FILE: 'Info.plist' From 9dd8f12a5b0f47a1afc726d621c8907ec357c02b Mon Sep 17 00:00:00 2001 From: Ruslan Serebriakov Date: Mon, 6 Feb 2023 04:26:20 +0000 Subject: [PATCH 092/426] [ERC4337] Transactions support (#2891) --- .../ethereum/TestEthereumTransactionSigner.kt | 231 +++++++++++++ src/Ethereum/EIP4337.cpp | 39 ++- src/Ethereum/EIP4337.h | 4 + src/Ethereum/MessageSigner.cpp | 20 +- src/Ethereum/MessageSigner.h | 1 + src/Ethereum/Signer.cpp | 137 ++++++++ src/Ethereum/Signer.h | 4 + src/Ethereum/Transaction.cpp | 192 ++++++++++- src/Ethereum/Transaction.h | 63 ++++ src/proto/Ethereum.proto | 35 +- swift/Tests/Blockchains/EthereumTests.swift | 261 ++++++++++++++- tests/chains/Ethereum/EIP4337Tests.cpp | 20 ++ tests/chains/Ethereum/SignerTests.cpp | 227 +++++++++++++ tests/chains/Ethereum/TWAnySignerTests.cpp | 315 ++++++++++++++++++ 14 files changed, 1509 insertions(+), 40 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumTransactionSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumTransactionSigner.kt index 61a6b9a42df..ed4c5a3d88f 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumTransactionSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumTransactionSigner.kt @@ -229,4 +229,235 @@ class TestEthereumTransactionSigner { assertEquals("f86a8084d693a400825208947d8bf18c7ce84b3e175b339c4ca93aed1dd166f1870348bca5a160008025a0fe5802b49e04c6b1705088310e133605ed8b549811a18968ad409ea02ad79f21a05bf845646fb1e1b9365f63a7fd5eb5e984094e3ed35c3bed7361aebbcbf41f10", result) } + + // EIP4337 + @Test + fun testEIP4337TransactionSigningAccountNotDeployed() { + val signingInput = Ethereum.SigningInput.newBuilder() + signingInput.apply { + privateKey = ByteString.copyFrom(PrivateKey("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8".toHexByteArray()).data()) + chainId = ByteString.copyFrom("0x5".toHexByteArray()) + nonce = ByteString.copyFrom("0x0".toHexByteArray()) + toAddress = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + txMode = TransactionMode.UserOp + + gasLimit = ByteString.copyFrom("0x5580".toHexByteArray()) + maxFeePerGas = ByteString.copyFrom("0x01952f1f85".toHexByteArray()) + maxInclusionFeePerGas = ByteString.copyFrom("0x0f".toHexByteArray()) + + userOperation = Ethereum.UserOperation.newBuilder().apply { + entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + isAccountDeployed = false + + preVerificationGas = ByteString.copyFrom("0xbc18".toHexByteArray()) + verificationGasLimit = ByteString.copyFrom("0x073272".toHexByteArray()) + }.build() + + transaction = Ethereum.Transaction.newBuilder().apply { + transfer = Ethereum.Transaction.Transfer.newBuilder().apply { + amount = ByteString.copyFrom("0x2386f26fc10000".toHexByteArray()) + }.build() + }.build() + } + + val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) + + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"21888\",\"initCode\":\"0x5a87209b755781cf65feeedd3855ade0317f4a925fbfb9cf00000000000000000000000078d9c32b96bb872d66d51818227563f44e67e2380000000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"6797860741\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"48152\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x1560b19d17613ec8580cb0feaf7ac2953771404c5bd7830f585e5062e6ddd4b82ae3bb8dbddb659c0300e8009857b5c77501e1cfd5bbab48d03de0ea7207d07c1b\",\"verificationGasLimit\":\"471666\"}"); + } + + @Test + fun testEIP4337TransactionSigningAccountDeployed() { + val signingInput = Ethereum.SigningInput.newBuilder() + signingInput.apply { + privateKey = ByteString.copyFrom(PrivateKey("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8".toHexByteArray()).data()) + chainId = ByteString.copyFrom("0x5".toHexByteArray()) + nonce = ByteString.copyFrom("0x1".toHexByteArray()) + + toAddress = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + txMode = TransactionMode.UserOp + + gasLimit = ByteString.copyFrom("0x9d55".toHexByteArray()) + maxFeePerGas = ByteString.copyFrom("0x1a339c9e9".toHexByteArray()) + maxInclusionFeePerGas = ByteString.copyFrom("0x0f".toHexByteArray()) + + userOperation = Ethereum.UserOperation.newBuilder().apply { + entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + isAccountDeployed = true + + preVerificationGas = ByteString.copyFrom("0xb708".toHexByteArray()) + verificationGasLimit = ByteString.copyFrom("0x186a0".toHexByteArray()) + }.build() + + transaction = Ethereum.Transaction.newBuilder().apply { + transfer = Ethereum.Transaction.Transfer.newBuilder().apply { + amount = ByteString.copyFrom("0x2386f26fc10000".toHexByteArray()) + }.build() + }.build() + } + + val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) + + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"40277\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"1\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0xaed2011e5cf267de495b38ecf86ad6f1d4c05217a99e59f47e8d52ba3d41c10144785893fa3e7c116a054999e3902fc2771064d0545148bc49f6d7c827fc7a9a1c\",\"verificationGasLimit\":\"100000\"}"); + } + + @Test + fun testEIP4337ERC20TransferAccountDeployed() { + val signingInput = Ethereum.SigningInput.newBuilder() + signingInput.apply { + privateKey = ByteString.copyFrom(PrivateKey("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8".toHexByteArray()).data()) + chainId = ByteString.copyFrom("0x5".toHexByteArray()) + nonce = ByteString.copyFrom("0x6".toHexByteArray()) + toAddress = "0x98339d8c260052b7ad81c28c16c0b98420f2b46a" + txMode = TransactionMode.UserOp + + gasLimit = ByteString.copyFrom("0xf78e".toHexByteArray()) + maxFeePerGas = ByteString.copyFrom("0x168ad5950f".toHexByteArray()) + maxInclusionFeePerGas = ByteString.copyFrom("0x0f".toHexByteArray()) + + userOperation = Ethereum.UserOperation.newBuilder().apply { + entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + isAccountDeployed = true + + preVerificationGas = ByteString.copyFrom("0xbb10".toHexByteArray()) + verificationGasLimit = ByteString.copyFrom("0x186a0".toHexByteArray()) + }.build() + + transaction = Ethereum.Transaction.newBuilder().apply { + erc20Transfer = Ethereum.Transaction.ERC20Transfer.newBuilder().apply { + amount = ByteString.copyFrom("0x186a0".toHexByteArray()) + to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + }.build() + }.build() + } + + val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) + + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f600000000000000000000000098339d8c260052b7ad81c28c16c0b98420f2b46a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"63374\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"6\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"47888\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0xd006c93d6a8753b5e7c1e6349de0dea34eab2e7a533106e0f2e1a3a3b013c8e97b007546dab9d7b8fc471ad14ff2e8aa351dc4f1ecb63bf20f33858dc7366cbe1c\",\"verificationGasLimit\":\"100000\"}"); + } + + @Test + fun testEIP4337ERC20ApproveAccountDeployed() { + val signingInput = Ethereum.SigningInput.newBuilder() + signingInput.apply { + privateKey = ByteString.copyFrom(PrivateKey("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8".toHexByteArray()).data()) + chainId = ByteString.copyFrom("0x5".toHexByteArray()) + nonce = ByteString.copyFrom("0x9".toHexByteArray()) + + toAddress = "0x98339d8c260052b7ad81c28c16c0b98420f2b46a" + txMode = TransactionMode.UserOp + + gasLimit = ByteString.copyFrom("0xf78e".toHexByteArray()) + maxFeePerGas = ByteString.copyFrom("0x168ad5950f".toHexByteArray()) + maxInclusionFeePerGas = ByteString.copyFrom("0x0f".toHexByteArray()) + + userOperation = Ethereum.UserOperation.newBuilder().apply { + entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + isAccountDeployed = true + + preVerificationGas = ByteString.copyFrom("0xbb10".toHexByteArray()) + verificationGasLimit = ByteString.copyFrom("0x186a0".toHexByteArray()) + }.build() + + transaction = Ethereum.Transaction.newBuilder().apply { + erc20Approve = Ethereum.Transaction.ERC20Approve.newBuilder().apply { + amount = ByteString.copyFrom("0x186a0".toHexByteArray()) + spender = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + }.build() + }.build() + } + + val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) + + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f600000000000000000000000098339d8c260052b7ad81c28c16c0b98420f2b46a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"63374\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"9\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"47888\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x262a67dd8cf3d16a72b7809b3b5ed55e9f4c2b93eedd5a3c6be035fbbd7111164464ec933d0fdfa359e266e318f3ac22702ae428ce14fc142e4475603e6ec15e1c\",\"verificationGasLimit\":\"100000\"}"); + } + + @Test + fun testEIP4337ERC721TransferAccountDeployed() { + val signingInput = Ethereum.SigningInput.newBuilder() + signingInput.apply { + privateKey = ByteString.copyFrom(PrivateKey("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8".toHexByteArray()).data()) + chainId = ByteString.copyFrom("0x5".toHexByteArray()) + nonce = ByteString.copyFrom("0xc".toHexByteArray()) + toAddress = "0xf5de760f2e916647fd766b4ad9e85ff943ce3a2b" + txMode = TransactionMode.UserOp + + gasLimit = ByteString.copyFrom("0x60B378".toHexByteArray()) + maxFeePerGas = ByteString.copyFrom("0x168ad5950f".toHexByteArray()) + maxInclusionFeePerGas = ByteString.copyFrom("0x0f".toHexByteArray()) + + userOperation = Ethereum.UserOperation.newBuilder().apply { + entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + isAccountDeployed = true + + preVerificationGas = ByteString.copyFrom("0xC34F".toHexByteArray()) + verificationGasLimit = ByteString.copyFrom("0x16E360".toHexByteArray()) + }.build() + + transaction = Ethereum.Transaction.newBuilder().apply { + erc721Transfer = Ethereum.Transaction.ERC721Transfer.newBuilder().apply { + tokenId = ByteString.copyFrom("0x2A8E57".toHexByteArray()) + from = "0x8cE23B8769ac01d0df0d5f47Be1A38FeA97F3879" + to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + }.build() + }.build() + } + + val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) + + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f6000000000000000000000000f5de760f2e916647fd766b4ad9e85ff943ce3a2b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006423b872dd0000000000000000000000008ce23b8769ac01d0df0d5f47be1a38fea97f3879000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000002a8e5700000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"6337400\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"12\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"49999\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x5951cc161a4d60d6b59503efb93e446f5d1a2e3a41d4503ba6393bcf2a2637340d0a865ed5d4d7650a68cbb95915eaa7ed54fd2c42b4bf7c83376f5c5d70691d1b\",\"verificationGasLimit\":\"1500000\"}"); + } + + @Test + fun testEIP4337ERC1155TransferAccountDeployed() { + val signingInput = Ethereum.SigningInput.newBuilder() + signingInput.apply { + privateKey = ByteString.copyFrom(PrivateKey("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8".toHexByteArray()).data()) + chainId = ByteString.copyFrom("0x5".toHexByteArray()) + nonce = ByteString.copyFrom("0x0".toHexByteArray()) + toAddress = "0x428ce4b916332e1afccfddce08baecc97cb40b12" + txMode = TransactionMode.UserOp + + gasLimit = ByteString.copyFrom("0x60B378".toHexByteArray()) + maxFeePerGas = ByteString.copyFrom("0x168ad5950f".toHexByteArray()) + maxInclusionFeePerGas = ByteString.copyFrom("0x0f".toHexByteArray()) + + userOperation = Ethereum.UserOperation.newBuilder().apply { + entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + accountFactory = "0x76627b8D1E01fAF0C73B69625BC1fCb8FA19a2AD" + accountLogic = "0x510ab68bd111ce7115df797118b0334d727d564b" + owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + isAccountDeployed = true + + preVerificationGas = ByteString.copyFrom("0xC738".toHexByteArray()) + verificationGasLimit = ByteString.copyFrom("0x16E360".toHexByteArray()) + }.build() + + transaction = Ethereum.Transaction.newBuilder().apply { + erc1155Transfer = Ethereum.Transaction.ERC1155Transfer.newBuilder().apply { + tokenId = ByteString.copyFrom("0x01".toHexByteArray()) + from = "0x8c560E00680b973645900528EDe71a99b8d4dca8" + to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + }.build() + }.build() + } + + val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) + + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f6000000000000000000000000428ce4b916332e1afccfddce08baecc97cb40b120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c4f242432a0000000000000000000000008c560e00680b973645900528ede71a99b8d4dca8000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"6337400\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"51000\",\"sender\":\"0x8c560e00680b973645900528ede71a99b8d4dca8\",\"signature\":\"0xaae38bcf9f946921541b44c2a66596968beecb9420471e2c9c531f758a2d652930ffdeeab95742e57e8520fb5c8ca4fee6a8e47e37336d4201fe104103f85e111c\",\"verificationGasLimit\":\"1500000\"}"); + } } diff --git a/src/Ethereum/EIP4337.cpp b/src/Ethereum/EIP4337.cpp index dd770fc16e5..e377f996c24 100644 --- a/src/Ethereum/EIP4337.cpp +++ b/src/Ethereum/EIP4337.cpp @@ -4,25 +4,54 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include "ABI.h" +#include "AddressChecksum.h" #include "EIP1014.h" #include "EIP1967.h" -#include "AddressChecksum.h" #include "Hash.h" #include "HexCoding.h" #include -#include "ABI.h" namespace TW::Ethereum { +using ParamBasePtr = std::shared_ptr; +using ParamCollection = std::vector; + +// https://github.com/thomas-waite/bundler/blob/b083680059a52d3121c5e33cea67c86652370562/packages/sdk/src/SimpleAccountAPI.ts#L63-L75 +Data getEIP4337AccountInitializeBytecode(const std::string& ownerAddress, const std::string& factoryAddress) { + auto createAccountFunc = ABI::Function("createAccount", ParamCollection{ + std::make_shared(parse_hex(ownerAddress)), + std::make_shared(0)}); + Data createAccountFuncEncoded; + createAccountFunc.encode(createAccountFuncEncoded); + + Data envelope; + append(envelope, parse_hex(factoryAddress)); + append(envelope, createAccountFuncEncoded); + return envelope; +} + +// https://github.com/eth-infinitism/account-abstraction/blob/5a1ad4072438d9e9f7c934b66464dc05a4b37d02/contracts/samples/SimpleAccountFactory.sol#L48 Data getEIP4337LogicInitializeBytecode(const std::string& ownerAddress) { - auto initializeFunc = ABI::Function("initialize", std::vector>{ - std::make_shared(parse_hex(ownerAddress)) - }); + auto initializeFunc = ABI::Function("initialize", ParamCollection{ + std::make_shared(parse_hex(ownerAddress))}); Data initializeFuncEncoded; initializeFunc.encode(initializeFuncEncoded); return initializeFuncEncoded; } +// https://github.com/thomas-waite/bundler/blob/b083680059a52d3121c5e33cea67c86652370562/packages/sdk/src/SimpleAccountAPI.ts#L91-L100 +Data getEIP4337ExecuteBytecode(const Data& toAddress, const uint256_t& value, const Data& data) { + auto executeFunc = ABI::Function("execute", ParamCollection{ + std::make_shared(toAddress), + std::make_shared(value), + std::make_shared(data)}); + Data executeFuncEncoded; + executeFunc.encode(executeFuncEncoded); + return executeFuncEncoded; +} + +// https://github.com/eth-infinitism/account-abstraction/blob/5a1ad4072438d9e9f7c934b66464dc05a4b37d02/contracts/samples/SimpleAccountFactory.sol#L43-L51 std::string getEIP4337DeploymentAddress(const std::string& factoryAddress, const std::string& logicAddress, const std::string& ownerAddress) { const Data logicInitializeBytecode = getEIP4337LogicInitializeBytecode(ownerAddress); const Data proxyInitCode = getEIP1967ProxyInitCode(logicAddress, logicInitializeBytecode); diff --git a/src/Ethereum/EIP4337.h b/src/Ethereum/EIP4337.h index 540bd2dd6e4..a72ca4eec55 100644 --- a/src/Ethereum/EIP4337.h +++ b/src/Ethereum/EIP4337.h @@ -7,10 +7,14 @@ #pragma once #include "Data.h" +#include "uint256.h" namespace TW::Ethereum { std::string getEIP4337DeploymentAddress(const std::string& factoryAddress, const std::string& logicAddress, const std::string& ownerAddress); + +Data getEIP4337AccountInitializeBytecode(const std::string& ownerAddress, const std::string& factoryAddress); Data getEIP4337LogicInitializeBytecode(const std::string& ownerAddress); +Data getEIP4337ExecuteBytecode(const Data& toAddress, const uint256_t& value, const Data& data); } diff --git a/src/Ethereum/MessageSigner.cpp b/src/Ethereum/MessageSigner.cpp index fa86a3097f2..1988cb55b5e 100644 --- a/src/Ethereum/MessageSigner.cpp +++ b/src/Ethereum/MessageSigner.cpp @@ -12,14 +12,6 @@ namespace TW::Ethereum::internal { -Data generateMessage(const std::string& message) { - std::string prefix(1, MessageSigner::EthereumPrefix); - std::stringstream ss; - ss << prefix << MessageSigner::MessagePrefix << std::to_string(message.size()) << message; - Data signableMessage = Hash::keccak256(data(ss.str())); - return signableMessage; -} - std::string commonSign(const PrivateKey& privateKey, const Data& signableMessage, MessageType msgType, TW::Ethereum::MessageSigner::MaybeChainId chainId) { auto data = privateKey.sign(signableMessage, TWCurveSECP256k1); switch (msgType) { @@ -40,8 +32,16 @@ std::string commonSign(const PrivateKey& privateKey, const Data& signableMessage namespace TW::Ethereum { +Data MessageSigner::generateMessage(const std::string& message) { + std::string prefix(1, MessageSigner::EthereumPrefix); + std::stringstream ss; + ss << prefix << MessageSigner::MessagePrefix << std::to_string(message.size()) << message; + Data signableMessage = Hash::keccak256(data(ss.str())); + return signableMessage; +} + std::string MessageSigner::signMessage(const PrivateKey& privateKey, const std::string& message, MessageType msgType, MaybeChainId chainId) { - auto signableMessage = internal::generateMessage(message); + auto signableMessage = generateMessage(message); return internal::commonSign(privateKey, signableMessage, msgType, chainId); } @@ -59,7 +59,7 @@ std::string MessageSigner::signTypedData(const PrivateKey& privateKey, const std } bool MessageSigner::verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept { - Data msg = internal::generateMessage(message); + Data msg = generateMessage(message); //! If it's json && EIP712Domain then we hash the struct if (nlohmann::json::accept(message)) { auto json = nlohmann::json::parse(message); diff --git a/src/Ethereum/MessageSigner.h b/src/Ethereum/MessageSigner.h index 4cb3c1e9598..cd82d9f8de6 100644 --- a/src/Ethereum/MessageSigner.h +++ b/src/Ethereum/MessageSigner.h @@ -45,6 +45,7 @@ class MessageSigner { static bool verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept; static constexpr auto MessagePrefix = "Ethereum Signed Message:\n"; static constexpr std::uint8_t EthereumPrefix{0x19}; + static Data generateMessage(const std::string& message); }; } // namespace TW::Ethereum diff --git a/src/Ethereum/Signer.cpp b/src/Ethereum/Signer.cpp index edfca7a0620..13ba33429ae 100644 --- a/src/Ethereum/Signer.cpp +++ b/src/Ethereum/Signer.cpp @@ -90,6 +90,19 @@ Signature Signer::signatureDataToStructSimple(const Data& signature) noexcept { return Signature{r, s, v}; } +Data Signer::simpleStructToSignatureData(const Signature& signature) noexcept { + Data fullSignature; + + auto r = store(signature.r, 32); + append(fullSignature, r); + auto s = store(signature.s, 32); + append(fullSignature, s); + auto v = store(signature.v, 1); + append(fullSignature, v); + + return fullSignature; +} + Signature Signer::signatureDataToStructWithEip155(const uint256_t& chainID, const Data& signature) noexcept { Signature rsv = signatureDataToStructSimple(signature); // Embed chainID in V param, for replay protection, legacy (EIP155) @@ -125,6 +138,17 @@ std::shared_ptr Signer::build(const Proto::SigningInput& input) uint256_t gasLimit = load(input.gas_limit()); uint256_t maxInclusionFeePerGas = load(input.max_inclusion_fee_per_gas()); uint256_t maxFeePerGas = load(input.max_fee_per_gas()); + + // EIP4337 + Data entryPointAddress = addressStringToData(input.user_operation().entry_point()); + Data accountFactoryAddress = addressStringToData(input.user_operation().account_factory()); + Data accountLogicAddress = addressStringToData(input.user_operation().account_logic()); + Data ownerAddress = addressStringToData(input.user_operation().owner()); + bool isAccountDeployed = input.user_operation().is_account_deployed(); + uint256_t preVerificationGas = load(input.user_operation().pre_verification_gas()); + uint256_t verificationGasLimit = load(input.user_operation().verification_gas_limit()); + Data paymasterAndData = Data(input.user_operation().paymaster_and_data().begin(), input.user_operation().paymaster_and_data().end()); + switch (input.transaction().transaction_oneof_case()) { case Proto::Transaction::kTransfer: { switch (input.tx_mode()) { @@ -142,6 +166,25 @@ std::shared_ptr Signer::build(const Proto::SigningInput& input) /* to: */ toAddress, /* amount: */ load(input.transaction().transfer().amount()), /* optional data: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); + + case Proto::TransactionMode::UserOp: + return UserOperation::buildNativeTransfer( + entryPointAddress, + accountFactoryAddress, + accountLogicAddress, + ownerAddress, + toAddress, + load(input.transaction().transfer().amount()), + nonce, + isAccountDeployed, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + paymasterAndData, + Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end()) + ); } } @@ -162,6 +205,24 @@ std::shared_ptr Signer::build(const Proto::SigningInput& input) /* tokenContract: */ toAddress, /* toAddress */ tokenToAddress, /* amount: */ load(input.transaction().erc20_transfer().amount())); + + case Proto::TransactionMode::UserOp: + return UserOperation::buildERC20Transfer( + entryPointAddress, + accountFactoryAddress, + accountLogicAddress, + ownerAddress, + toAddress, + tokenToAddress, + load(input.transaction().erc20_transfer().amount()), + nonce, + isAccountDeployed, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + paymasterAndData); } } @@ -182,6 +243,24 @@ std::shared_ptr Signer::build(const Proto::SigningInput& input) /* tokenContract: */ toAddress, /* toAddress */ spenderAddress, /* amount: */ load(input.transaction().erc20_approve().amount())); + + case Proto::TransactionMode::UserOp: + return UserOperation::buildERC20Approve( + entryPointAddress, + accountFactoryAddress, + accountLogicAddress, + ownerAddress, + toAddress, + spenderAddress, + load(input.transaction().erc20_approve().amount()), + nonce, + isAccountDeployed, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + paymasterAndData); } } @@ -205,6 +284,25 @@ std::shared_ptr Signer::build(const Proto::SigningInput& input) /* fromAddress: */ tokenFromAddress, /* toAddress */ tokenToAddress, /* tokenId: */ load(input.transaction().erc721_transfer().token_id())); + + case Proto::TransactionMode::UserOp: + return UserOperation::buildERC721Transfer( + entryPointAddress, + accountFactoryAddress, + accountLogicAddress, + ownerAddress, + toAddress, + tokenFromAddress, + tokenToAddress, + load(input.transaction().erc721_transfer().token_id()), + nonce, + isAccountDeployed, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + paymasterAndData); } } @@ -232,6 +330,27 @@ std::shared_ptr Signer::build(const Proto::SigningInput& input) /* tokenId: */ load(input.transaction().erc1155_transfer().token_id()), /* value */ load(input.transaction().erc1155_transfer().value()), /* data */ Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end())); + + case Proto::TransactionMode::UserOp: + return UserOperation::buildERC1155Transfer( + entryPointAddress, + accountFactoryAddress, + accountLogicAddress, + ownerAddress, + toAddress, + tokenFromAddress, + tokenToAddress, + load(input.transaction().erc1155_transfer().token_id()), + load(input.transaction().erc1155_transfer().value()), + Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end()), + nonce, + isAccountDeployed, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + paymasterAndData); } } @@ -252,6 +371,24 @@ std::shared_ptr Signer::build(const Proto::SigningInput& input) /* to: */ toAddress, /* amount: */ load(input.transaction().contract_generic().amount()), /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); + + case Proto::TransactionMode::UserOp: + return UserOperation::buildNativeTransfer( + entryPointAddress, + accountFactoryAddress, + accountLogicAddress, + ownerAddress, + toAddress, + load(input.transaction().contract_generic().amount()), + nonce, + isAccountDeployed, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + paymasterAndData, + Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); } } } diff --git a/src/Ethereum/Signer.h b/src/Ethereum/Signer.h index 3fb42172c1d..cdf32672ec9 100644 --- a/src/Ethereum/Signer.h +++ b/src/Ethereum/Signer.h @@ -54,6 +54,10 @@ class Signer { /// \returns the r, s, and v values of the transaction signature static Signature signatureDataToStructSimple(const Data& signature) noexcept; + /// Converts R, S, and V values into the full signature, with no replay protection. + /// \returns the full signature bytes + static Data simpleStructToSignatureData(const Signature& signature) noexcept; + /// Break up the signature into the R, S, and V values, and include chainID in V for replay protection (Eip155) /// \returns the r, s, and v values of the transaction signature static Signature signatureDataToStructWithEip155(const uint256_t& chainID, const Data& signature) noexcept; diff --git a/src/Ethereum/Transaction.cpp b/src/Ethereum/Transaction.cpp index d033dbb6f80..bb168b91cee 100644 --- a/src/Ethereum/Transaction.cpp +++ b/src/Ethereum/Transaction.cpp @@ -5,16 +5,24 @@ // file LICENSE at the root of the source code distribution tree. #include "Transaction.h" -#include "ABI/Function.h" -#include "ABI/ParamAddress.h" -#include "ABI/ParamBase.h" +#include "Ethereum/ABI.h" #include "HexCoding.h" #include "RLP.h" +#include "Signer.h" +#include +#include +#include namespace TW::Ethereum { +using json = nlohmann::json; +using ParamBasePtr = std::shared_ptr; +using ParamCollection = std::vector; +using UserOperationPtr = std::shared_ptr; + static const Data EmptyListEncoded = parse_hex("c0"); +/// TransactionNonTyped std::shared_ptr TransactionNonTyped::buildNativeTransfer(const uint256_t& nonce, const uint256_t& gasPrice, const uint256_t& gasLimit, @@ -84,7 +92,7 @@ Data TransactionNonTyped::encoded(const Signature& signature, [[maybe_unused]] c Data TransactionNonTyped::buildERC20TransferCall(const Data& to, const uint256_t& amount) { // clang-format off - auto func = ABI::Function("transfer", std::vector>{ + auto func = ABI::Function("transfer", ParamCollection{ std::make_shared(to), std::make_shared(amount) }); @@ -96,7 +104,7 @@ Data TransactionNonTyped::buildERC20TransferCall(const Data& to, const uint256_t Data TransactionNonTyped::buildERC20ApproveCall(const Data& spender, const uint256_t& amount) { // clang-format off - auto func = ABI::Function("approve", std::vector>{ + auto func = ABI::Function("approve", ParamCollection{ std::make_shared(spender), std::make_shared(amount) }); @@ -108,7 +116,7 @@ Data TransactionNonTyped::buildERC20ApproveCall(const Data& spender, const uint2 Data TransactionNonTyped::buildERC721TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId) { // clang-format off - auto func = ABI::Function("transferFrom", std::vector>{ + auto func = ABI::Function("transferFrom", ParamCollection{ std::make_shared(from), std::make_shared(to), std::make_shared(tokenId) @@ -121,7 +129,7 @@ Data TransactionNonTyped::buildERC721TransferFromCall(const Data& from, const Da Data TransactionNonTyped::buildERC1155TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { // clang-format off - auto func = ABI::Function("safeTransferFrom", std::vector>{ + auto func = ABI::Function("safeTransferFrom", ParamCollection{ std::make_shared(from), std::make_shared(to), std::make_shared(tokenId), @@ -134,6 +142,7 @@ Data TransactionNonTyped::buildERC1155TransferFromCall(const Data& from, const D return payload; } +/// TransactionEip1559 Data TransactionEip1559::preHash(const uint256_t chainID) const { return Hash::keccak256(serialize(chainID)); } @@ -212,4 +221,171 @@ TransactionEip1559::buildERC1155Transfer(const uint256_t& nonce, return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC1155TransferFromCall(from, to, tokenId, value, data)); } -} // namespace TW::Ethereum +/// UserOperation +Data UserOperation::preHash(const uint256_t chainID) const { + auto params = ABI::ParamTuple(ParamCollection{ + std::make_shared(32, Hash::keccak256(serialize(chainID))), + std::make_shared(entryPoint), + std::make_shared(chainID)}); + Data encoded; + params.encode(encoded); + const auto hash = Hash::keccak256(encoded); + const auto hashStr = std::string(hash.begin(), hash.end()); + return MessageSigner::generateMessage(hashStr); +} + +Data UserOperation::serialize(const uint256_t chainID) const { + auto params = ABI::ParamTuple(ParamCollection{ + std::make_shared(sender), + std::make_shared(nonce), + std::make_shared(initCode), + std::make_shared(payload), + std::make_shared(gasLimit), + std::make_shared(verificationGasLimit), + std::make_shared(preVerificationGas), + std::make_shared(maxFeePerGas), + std::make_shared(maxInclusionFeePerGas), + std::make_shared(paymasterAndData), + std::make_shared(parse_hex("0x"))}); + Data serialized; + params.encode(serialized); + + return Data(serialized.begin(), serialized.end() - 32); // remove trailing word (zero-length signature) +} + +Data UserOperation::encoded(const Signature& signature, const uint256_t chainID) const { + Data rawSignature = Signer::simpleStructToSignatureData(signature); + rawSignature[64] += 27; + + const json tx = { + {"sender", hexEncoded(sender)}, + {"nonce", nonce.str()}, + {"initCode", hexEncoded(initCode)}, + {"callData", hexEncoded(payload)}, + {"callGasLimit", gasLimit.str()}, + {"verificationGasLimit", verificationGasLimit.str()}, + {"maxFeePerGas", maxFeePerGas.str()}, + {"maxPriorityFeePerGas", maxInclusionFeePerGas.str()}, + {"paymasterAndData", hexEncoded(paymasterAndData)}, + {"preVerificationGas", preVerificationGas.str()}, + {"signature", hexEncoded(rawSignature)}}; + const auto txString = tx.dump(); + return Data(txString.begin(), txString.end()); +} + +UserOperationPtr +UserOperation::buildNativeTransfer(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& toAddress, const uint256_t& amount, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData, const Data& payload) { + Data initCode = {}; + if (!isAccountDeployed) { + initCode = Ethereum::getEIP4337AccountInitializeBytecode(hex(ownerAddress), hex(factoryAddress)); + } + return std::make_shared( + entryPointAddress, + parse_hex(Ethereum::getEIP4337DeploymentAddress(hex(factoryAddress), hex(logicAddress), hex(ownerAddress))), + nonce, + initCode, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + Ethereum::getEIP4337ExecuteBytecode(toAddress, amount, payload), + paymasterAndData); +} + +UserOperationPtr +UserOperation::buildERC20Transfer(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& tokenContract, const Data& toAddress, const uint256_t& amount, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData) { + Data initCode = {}; + if (!isAccountDeployed) { + initCode = Ethereum::getEIP4337AccountInitializeBytecode(hex(ownerAddress), hex(factoryAddress)); + } + return std::make_shared( + entryPointAddress, + parse_hex(Ethereum::getEIP4337DeploymentAddress(hex(factoryAddress), hex(logicAddress), hex(ownerAddress))), + nonce, + initCode, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + Ethereum::getEIP4337ExecuteBytecode(tokenContract, 0, TransactionNonTyped::buildERC20TransferCall(toAddress, amount)), + paymasterAndData); +} + +UserOperationPtr +UserOperation::buildERC20Approve(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData) { + Data initCode = {}; + if (!isAccountDeployed) { + initCode = Ethereum::getEIP4337AccountInitializeBytecode(hex(ownerAddress), hex(factoryAddress)); + } + return std::make_shared( + entryPointAddress, + parse_hex(Ethereum::getEIP4337DeploymentAddress(hex(factoryAddress), hex(logicAddress), hex(ownerAddress))), + nonce, + initCode, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + Ethereum::getEIP4337ExecuteBytecode(tokenContract, 0, TransactionNonTyped::buildERC20ApproveCall(spenderAddress, amount)), + paymasterAndData); +} + +UserOperationPtr +UserOperation::buildERC721Transfer(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData) { + Data initCode = {}; + if (!isAccountDeployed) { + initCode = Ethereum::getEIP4337AccountInitializeBytecode(hex(ownerAddress), hex(factoryAddress)); + } + return std::make_shared( + entryPointAddress, + parse_hex(Ethereum::getEIP4337DeploymentAddress(hex(factoryAddress), hex(logicAddress), hex(ownerAddress))), + nonce, + initCode, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + Ethereum::getEIP4337ExecuteBytecode(tokenContract, 0, TransactionNonTyped::buildERC721TransferFromCall(from, to, tokenId)), + paymasterAndData); +} + +UserOperationPtr +UserOperation::buildERC1155Transfer(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData) { + Data initCode = {}; + if (!isAccountDeployed) { + initCode = Ethereum::getEIP4337AccountInitializeBytecode(hex(ownerAddress), hex(factoryAddress)); + } + return std::make_shared( + entryPointAddress, + parse_hex(Ethereum::getEIP4337DeploymentAddress(hex(factoryAddress), hex(logicAddress), hex(ownerAddress))), + nonce, + initCode, + gasLimit, + verificationGasLimit, + maxFeePerGas, + maxInclusionFeePerGas, + preVerificationGas, + Ethereum::getEIP4337ExecuteBytecode(tokenContract, 0, TransactionNonTyped::buildERC1155TransferFromCall(from, to, tokenId, value, data)), + paymasterAndData); +} + +} // namespace TW::Ethereum \ No newline at end of file diff --git a/src/Ethereum/Transaction.h b/src/Ethereum/Transaction.h index d43fd77aa10..0cf47190e3d 100644 --- a/src/Ethereum/Transaction.h +++ b/src/Ethereum/Transaction.h @@ -108,6 +108,7 @@ class TransactionNonTyped: public TransactionBase { enum TransactionType: uint8_t { TxType_OptionalAccessList = 0x01, TxType_Eip1559 = 0x02, + TxType_Eip4337 = 0x03, }; /// Base class for various typed transactions. @@ -169,4 +170,66 @@ class TransactionEip1559: public TransactionTyped { , amount(std::move(amount)) {} }; +/// EIP4337 UserOperation +// https://github.com/ethereum/EIPs/blob/3fd65b1a782912bfc18cb975c62c55f733c7c96e/EIPS/eip-4337.md#specification +class UserOperation: public TransactionTyped { + using UserOperationPtr = std::shared_ptr; +public: + Data entryPoint; + Data sender; + Data initCode; + uint256_t gasLimit; + uint256_t verificationGasLimit; + uint256_t maxFeePerGas; + uint256_t maxInclusionFeePerGas; + uint256_t preVerificationGas; + Data paymasterAndData; + + // Factory methods + // Create a native transfer transaction + static UserOperationPtr buildNativeTransfer(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& toAddress, const uint256_t& amount, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData = {}, const Data& payload = {}); + // Create an ERC20 token transfer transaction + static UserOperationPtr buildERC20Transfer(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& tokenContract, const Data& toAddress, const uint256_t& amount, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData = {}); + // Create an ERC20 approve transaction + static UserOperationPtr buildERC20Approve(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData = {}); + // Create an ERC721 NFT transfer transaction + static UserOperationPtr buildERC721Transfer(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData = {}); + // Create an ERC1155 NFT transfer transaction + static UserOperationPtr buildERC1155Transfer(const Data& entryPointAddress, const Data& factoryAddress, const Data& logicAddress, const Data& ownerAddress, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data, const uint256_t& nonce, const bool& isAccountDeployed, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& paymasterAndData = {}); + + virtual Data preHash(const uint256_t chainID) const; + virtual Data serialize(const uint256_t chainID) const; + virtual Data encoded(const Signature& signature, const uint256_t chainID) const; + +public: + UserOperation(const Data& entryPoint, const Data& sender, const uint256_t& nonce, const Data& initCode, + const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, + const Data& payload = {}, const Data& paymasterAndData = {}) + : TransactionTyped(TxType_Eip4337, nonce, payload) + , entryPoint(std::move(entryPoint)) + , sender(std::move(sender)) + , initCode(std::move(initCode)) + , gasLimit(std::move(gasLimit)) + , verificationGasLimit(std::move(verificationGasLimit)) + , maxFeePerGas(std::move(maxFeePerGas)) + , maxInclusionFeePerGas(std::move(maxInclusionFeePerGas)) + , preVerificationGas(std::move(preVerificationGas)) + , paymasterAndData(std::move(paymasterAndData)) {} +}; + } // namespace TW::Ethereum diff --git a/src/proto/Ethereum.proto b/src/proto/Ethereum.proto index ce3ab148556..b84af3e1a70 100644 --- a/src/proto/Ethereum.proto +++ b/src/proto/Ethereum.proto @@ -89,7 +89,37 @@ enum TransactionMode { Legacy = 0; // Enveloped transaction EIP2718 (with type 0x2), fee is according to EIP1559 (base fee, inclusion fee, ...) - Enveloped = 1; + Enveloped = 1; + + // EIP4337-compatible UserOperation + UserOp = 2; +} + +// ERC-4337 structure that describes a transaction to be sent on behalf of a user +message UserOperation { + // Entry point contract address + string entry_point = 1; + + // Account factory contract address + string account_factory = 2; + + // Account logic contract address + string account_logic = 3; + + // Public address of the account signer + string owner = 4; + + // Whether the smart contract for this address has already been deployed + bool is_account_deployed = 5; + + // The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata + bytes pre_verification_gas = 6; + + // The amount of gas to allocate for the verification step + bytes verification_gas_limit = 7; + + // Address of paymaster sponsoring the transaction, followed by extra data to send to the paymaster (empty for self-sponsored transaction) + bytes paymaster_and_data = 8; } // Input data necessary to create a signed transaction. @@ -128,6 +158,9 @@ message SigningInput { // The payload transaction Transaction transaction = 10; + + // UserOperation for ERC-4337 wallets + UserOperation user_operation = 11; } // Result containing the signed and encoded transaction. diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index 4a94e459445..8a67d5e9756 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -8,22 +8,6 @@ import XCTest import WalletCore class EthereumTests: XCTestCase { - - func testCreate2Address() { - let address = "0x0000000000000000000000000000000000000000" - let salt = Data(hexString: "0x0000000000000000000000000000000000000000000000000000000000000000")! - let initCodeHash = Hash.keccak256(data: Data(hexString: "0x00")!) - let result = Ethereum.eip1014AddressCreate2(fromEthAddress: address, salt: salt, initCodeHash: initCodeHash) - XCTAssertEqual(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38") - } - - func testEIP4337DeploymentAddress() { - let factoryAddress = "0xd9145CCE52D386f254917e481eB44e9943F39138" - let logicAddress = "0x5C9eb5D6a6C2c1B3EFc52255C0b356f116f6f66D" - let ownerAddress = "0xA5a1dddEF094095AfB7b6e322dE72961DF2e1988" - let result = Ethereum.eip4337GetDeploymentAddress(factoryAddress: factoryAddress, logicAddress: logicAddress, ownerAddress: ownerAddress) - XCTAssertEqual(result, "0xbEaA87cEEaC906C21aaacd258FbFB87CfA3c90a8") - } func testAddress() { let anyAddress = AnyAddress(string: "0x7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1", coin: .ethereum) @@ -387,4 +371,249 @@ class EthereumTests: XCTestCase { let pubKey = privateKey.getPublicKey(coinType: .ethereum) XCTAssertTrue(EthereumMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) } + + // EIP4337 + + func testCreate2Address() { + let address = "0x0000000000000000000000000000000000000000" + let salt = Data(hexString: "0x0000000000000000000000000000000000000000000000000000000000000000")! + let initCodeHash = Hash.keccak256(data: Data(hexString: "0x00")!) + let result = Ethereum.eip1014AddressCreate2(fromEthAddress: address, salt: salt, initCodeHash: initCodeHash) + XCTAssertEqual(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38") + } + + func testEIP4337DeploymentAddress() { + let factoryAddress = "0xd9145CCE52D386f254917e481eB44e9943F39138" + let logicAddress = "0x5C9eb5D6a6C2c1B3EFc52255C0b356f116f6f66D" + let ownerAddress = "0xA5a1dddEF094095AfB7b6e322dE72961DF2e1988" + let result = Ethereum.eip4337GetDeploymentAddress(factoryAddress: factoryAddress, logicAddress: logicAddress, ownerAddress: ownerAddress) + XCTAssertEqual(result, "0xbEaA87cEEaC906C21aaacd258FbFB87CfA3c90a8") + } + + func testEIP4337NativeTransferAccountNotDeployed() throws { + let input = EthereumSigningInput.with { + $0.txMode = .userOp + $0.chainID = Data(hexString: "05")! + $0.nonce = Data(hexString: "00")! + $0.toAddress = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + + $0.gasLimit = Data(hexString: "0x5580")! + $0.maxFeePerGas = Data(hexString: "0x01952f1f85")! + $0.maxInclusionFeePerGas = Data(hexString: "0x0f")! + + $0.userOperation = EthereumUserOperation.with { + $0.entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + $0.accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + $0.accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + $0.owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + $0.isAccountDeployed = false + + $0.preVerificationGas = Data(hexString: "0xbc18")! + $0.verificationGasLimit = Data(hexString: "0x073272")! + } + + $0.privateKey = Data(hexString: "0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")! + $0.transaction = EthereumTransaction.with { + $0.transfer = EthereumTransaction.Transfer.with { + $0.amount = Data(hexString: "0x2386f26fc10000")! + } + } + } + + let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + let json = String(data: output.encoded, encoding: .utf8) + + XCTAssertEqual(try input.serializedData().hexString, "0a010512010018022a02558032010f3a0501952f1f85422a3078636536343233353546613535336634303843333461323635304164324634413136333464303333614a20f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8520b0a090a072386f26fc100005ab9010a2a307831333036623031624333653441443230323631324433383433333837653934373337363733463533122a3078354138373230396237353537383163463635664565456464333835356164653033313766346139321a2a307832316363323764376462346661313938353761333730323635336137613637656533306361363230222a3078373864394333326239364262383732443636443531383138323237353633663434653637453233383202bc183a03073272") + XCTAssertEqual(json, "{\"callData\":\"0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"21888\",\"initCode\":\"0x5a87209b755781cf65feeedd3855ade0317f4a925fbfb9cf00000000000000000000000078d9c32b96bb872d66d51818227563f44e67e2380000000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"6797860741\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"48152\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x1560b19d17613ec8580cb0feaf7ac2953771404c5bd7830f585e5062e6ddd4b82ae3bb8dbddb659c0300e8009857b5c77501e1cfd5bbab48d03de0ea7207d07c1b\",\"verificationGasLimit\":\"471666\"}"); + } + + func testEIP4337NativeTransferAccountDeployed() throws { + let input = EthereumSigningInput.with { + $0.txMode = .userOp + $0.chainID = Data(hexString: "05")! + $0.nonce = Data(hexString: "01")! + $0.toAddress = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + + $0.gasLimit = Data(hexString: "0x9d55")! + $0.maxFeePerGas = Data(hexString: "0x01a339c9e9")! + $0.maxInclusionFeePerGas = Data(hexString: "0x0f")! + + $0.userOperation = EthereumUserOperation.with { + $0.entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + $0.accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + $0.accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + $0.owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + $0.isAccountDeployed = true + + $0.preVerificationGas = Data(hexString: "0xb708")! + $0.verificationGasLimit = Data(hexString: "0x0186a0")! + } + + $0.privateKey = Data(hexString: "0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")! + $0.transaction = EthereumTransaction.with { + $0.transfer = EthereumTransaction.Transfer.with { + $0.amount = Data(hexString: "0x2386f26fc10000")! + } + } + } + + let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + let json = String(data: output.encoded, encoding: .utf8) + + XCTAssertEqual(try input.serializedData().hexString, "0a010512010118022a029d5532010f3a0501a339c9e9422a3078636536343233353546613535336634303843333461323635304164324634413136333464303333614a20f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8520b0a090a072386f26fc100005abb010a2a307831333036623031624333653441443230323631324433383433333837653934373337363733463533122a3078354138373230396237353537383163463635664565456464333835356164653033313766346139321a2a307832316363323764376462346661313938353761333730323635336137613637656533306361363230222a30783738643943333262393642623837324436364435313831383232373536336634346536374532333828013202b7083a030186a0") + XCTAssertEqual(json, "{\"callData\":\"0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"40277\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"1\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0xaed2011e5cf267de495b38ecf86ad6f1d4c05217a99e59f47e8d52ba3d41c10144785893fa3e7c116a054999e3902fc2771064d0545148bc49f6d7c827fc7a9a1c\",\"verificationGasLimit\":\"100000\"}"); + } + + func testEIP4337ERC20TransferAccountDeployed() throws { + let input = EthereumSigningInput.with { + $0.txMode = .userOp + $0.chainID = Data(hexString: "05")! + $0.nonce = Data(hexString: "06")! + $0.toAddress = "0x98339d8c260052b7ad81c28c16c0b98420f2b46a" + + $0.gasLimit = Data(hexString: "0xf78e")! + $0.maxFeePerGas = Data(hexString: "0x168ad5950f")! + $0.maxInclusionFeePerGas = Data(hexString: "0x0f")! + + + $0.userOperation = EthereumUserOperation.with { + $0.entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + $0.accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + $0.accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + $0.owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + $0.isAccountDeployed = true + + $0.preVerificationGas = Data(hexString: "0xbb10")! + $0.verificationGasLimit = Data(hexString: "0x0186a0")! + } + + $0.privateKey = Data(hexString: "0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")! + $0.transaction = EthereumTransaction.with { + $0.erc20Transfer = EthereumTransaction.ERC20Transfer.with { + $0.to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + $0.amount = Data(hexString: "0x0186a0")! + } + } + } + + let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + let json = String(data: output.encoded, encoding: .utf8) + + XCTAssertEqual(json, "{\"callData\":\"0xb61d27f600000000000000000000000098339d8c260052b7ad81c28c16c0b98420f2b46a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"63374\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"6\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"47888\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0xd006c93d6a8753b5e7c1e6349de0dea34eab2e7a533106e0f2e1a3a3b013c8e97b007546dab9d7b8fc471ad14ff2e8aa351dc4f1ecb63bf20f33858dc7366cbe1c\",\"verificationGasLimit\":\"100000\"}"); + } + + func testEIP4337ERC20ApproveAccountDeployed() throws { + let input = EthereumSigningInput.with { + $0.txMode = .userOp + $0.chainID = Data(hexString: "05")! + $0.nonce = Data(hexString: "09")! + $0.toAddress = "0x98339d8c260052b7ad81c28c16c0b98420f2b46a" + + $0.gasLimit = Data(hexString: "0xf78e")! + $0.maxFeePerGas = Data(hexString: "0x168ad5950f")! + $0.maxInclusionFeePerGas = Data(hexString: "0x0f")! + + $0.userOperation = EthereumUserOperation.with { + $0.entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + $0.accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + $0.accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + $0.owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + $0.isAccountDeployed = true + + $0.preVerificationGas = Data(hexString: "0xbb10")! + $0.verificationGasLimit = Data(hexString: "0x0186a0")! + } + + $0.privateKey = Data(hexString: "0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")! + $0.transaction = EthereumTransaction.with { + $0.erc20Approve = EthereumTransaction.ERC20Approve.with { + $0.spender = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + $0.amount = Data(hexString: "0x0186a0")! + } + } + } + + let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + let json = String(data: output.encoded, encoding: .utf8) + + XCTAssertEqual(json, "{\"callData\":\"0xb61d27f600000000000000000000000098339d8c260052b7ad81c28c16c0b98420f2b46a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"63374\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"9\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"47888\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x262a67dd8cf3d16a72b7809b3b5ed55e9f4c2b93eedd5a3c6be035fbbd7111164464ec933d0fdfa359e266e318f3ac22702ae428ce14fc142e4475603e6ec15e1c\",\"verificationGasLimit\":\"100000\"}"); + } + + func testEIP4337ERC721TransferAccountDeployed() throws { + let input = EthereumSigningInput.with { + $0.txMode = .userOp + $0.chainID = Data(hexString: "05")! + $0.nonce = Data(hexString: "0x0C")! + $0.toAddress = "0xf5de760f2e916647fd766b4ad9e85ff943ce3a2b" + + $0.gasLimit = Data(hexString: "0x60B378")! + $0.maxFeePerGas = Data(hexString: "0x168ad5950f")! + $0.maxInclusionFeePerGas = Data(hexString: "0x0f")! + + $0.userOperation = EthereumUserOperation.with { + $0.entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + $0.accountFactory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92" + $0.accountLogic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620" + $0.owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + $0.isAccountDeployed = true + + $0.preVerificationGas = Data(hexString: "0xC34F")! + $0.verificationGasLimit = Data(hexString: "0x16E360")! + } + + $0.privateKey = Data(hexString: "0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")! + $0.transaction = EthereumTransaction.with { + $0.erc721Transfer = EthereumTransaction.ERC721Transfer.with { + $0.from = "0x8cE23B8769ac01d0df0d5f47Be1A38FeA97F3879" + $0.to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + $0.tokenID = Data(hexString: "0x2A8E57")! + } + } + } + + let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + let json = String(data: output.encoded, encoding: .utf8) + + XCTAssertEqual(json, "{\"callData\":\"0xb61d27f6000000000000000000000000f5de760f2e916647fd766b4ad9e85ff943ce3a2b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006423b872dd0000000000000000000000008ce23b8769ac01d0df0d5f47be1a38fea97f3879000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000002a8e5700000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"6337400\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"12\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"49999\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x5951cc161a4d60d6b59503efb93e446f5d1a2e3a41d4503ba6393bcf2a2637340d0a865ed5d4d7650a68cbb95915eaa7ed54fd2c42b4bf7c83376f5c5d70691d1b\",\"verificationGasLimit\":\"1500000\"}"); + } + + func testEIP4337ERC1155TransferAccountDeployed() throws { + let input = EthereumSigningInput.with { + $0.txMode = .userOp + $0.chainID = Data(hexString: "05")! + $0.nonce = Data(hexString: "0x")! + $0.toAddress = "0x428ce4b916332e1afccfddce08baecc97cb40b12" + + $0.gasLimit = Data(hexString: "0x60B378")! + $0.maxFeePerGas = Data(hexString: "0x168ad5950f")! + $0.maxInclusionFeePerGas = Data(hexString: "0x0f")! + + $0.userOperation = EthereumUserOperation.with { + $0.entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53" + $0.accountFactory = "0x76627b8D1E01fAF0C73B69625BC1fCb8FA19a2AD" + $0.accountLogic = "0x510ab68bd111ce7115df797118b0334d727d564b" + $0.owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238" + $0.isAccountDeployed = true + + $0.preVerificationGas = Data(hexString: "0xC738")! + $0.verificationGasLimit = Data(hexString: "0x16E360")! + } + + $0.privateKey = Data(hexString: "0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")! + $0.transaction = EthereumTransaction.with { + $0.erc1155Transfer = EthereumTransaction.ERC1155Transfer.with { + $0.from = "0x8c560E00680b973645900528EDe71a99b8d4dca8" + $0.to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a" + $0.tokenID = Data(hexString: "0x01")! + $0.value = Data(hexString: "0x")! + $0.data = Data(hexString: "0x")! + } + } + } + + let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + let json = String(data: output.encoded, encoding: .utf8) + + XCTAssertEqual(json, "{\"callData\":\"0xb61d27f6000000000000000000000000428ce4b916332e1afccfddce08baecc97cb40b120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c4f242432a0000000000000000000000008c560e00680b973645900528ede71a99b8d4dca8000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"6337400\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"51000\",\"sender\":\"0x8c560e00680b973645900528ede71a99b8d4dca8\",\"signature\":\"0xaae38bcf9f946921541b44c2a66596968beecb9420471e2c9c531f758a2d652930ffdeeab95742e57e8520fb5c8ca4fee6a8e47e37336d4201fe104103f85e111c\",\"verificationGasLimit\":\"1500000\"}"); + } } diff --git a/tests/chains/Ethereum/EIP4337Tests.cpp b/tests/chains/Ethereum/EIP4337Tests.cpp index 294a3dc75e5..0d702aabde4 100644 --- a/tests/chains/Ethereum/EIP4337Tests.cpp +++ b/tests/chains/Ethereum/EIP4337Tests.cpp @@ -15,6 +15,15 @@ namespace TW::Ethereum::tests { +TEST(EthereumEip4337, GetEIP4337AccountInitializeBytecode) { + { + const std::string& ownerAddress = "0x78d9C32b96Bb872D66D51818227563f44e67E238"; + const std::string& factoryAddress = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92"; + const auto& initializeEncoded = Ethereum::getEIP4337AccountInitializeBytecode(ownerAddress, factoryAddress); + ASSERT_EQ(hexEncoded(initializeEncoded), "0x5a87209b755781cf65feeedd3855ade0317f4a925fbfb9cf00000000000000000000000078d9c32b96bb872d66d51818227563f44e67e2380000000000000000000000000000000000000000000000000000000000000000"); + } +} + TEST(EthereumEip4337, GetEIP4337LogicInitializeBytecode) { { const std::string& ownerAddress = "0xA5a1dddEF094095AfB7b6e322dE72961DF2e1988"; @@ -23,6 +32,17 @@ TEST(EthereumEip4337, GetEIP4337LogicInitializeBytecode) { } } +TEST(EthereumEip4337, GetEIP4337ExecuteBytecode) { + { + const Data& toAddress = parse_hex("0xce642355Fa553f408C34a2650Ad2F4A1634d033a"); + const uint256_t& value = 0x2386f26fc10000; + const Data data = {}; + const auto& executeEncoded = Ethereum::getEIP4337ExecuteBytecode(toAddress, value, data); + ASSERT_EQ(hexEncoded(executeEncoded), "0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"); + } +} + +// // https://goerli.etherscan.io/address/0x5a87209b755781cf65feeedd3855ade0317f4a92#readContract TEST(EthereumEip4337, GetEIP4337DeploymentAddress) { // C++ diff --git a/tests/chains/Ethereum/SignerTests.cpp b/tests/chains/Ethereum/SignerTests.cpp index bb938626569..14de9e19dc9 100644 --- a/tests/chains/Ethereum/SignerTests.cpp +++ b/tests/chains/Ethereum/SignerTests.cpp @@ -109,4 +109,231 @@ TEST(EthereumSigner, EIP1559_1442) { EXPECT_EQ(hex(encoded), "02f8710306847735940084b2d05e0082526c94b9f5771c27664bf2282d98e09d7f50cec7cb01a78701ee0c29f50cb180c080a092c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64a06487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"); } +TEST(EthereumSigner, EIP4337_NativeTransfer_Account_Not_Deployed) { + const uint256_t chainID = 5; + const auto transaction = UserOperation::buildNativeTransfer( + parse_hex("0x1306b01bC3e4AD202612D3843387e94737673F53"), + parse_hex("0x5A87209b755781cF65fEeEdd3855ade0317f4a92"), + parse_hex("0x21cc27d7db4fa19857a3702653a7a67ee30ca620"), + parse_hex("0x78d9C32b96Bb872D66D51818227563f44e67E238"), + parse_hex("0xce642355Fa553f408C34a2650Ad2F4A1634d033a"), + 0x2386f26fc10000, + 0x00, + false, + 0x5580, + 0x073272, + 0x01952f1f85, + 0x0f, + 0xbc18 + ); + + const auto serialized = transaction->serialize(chainID); + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto signature = Signer::sign(key, chainID, transaction); + const auto encoded = transaction->encoded(signature, chainID); + const auto result = std::string(encoded.begin(), encoded.end()); + + // https://goerli.etherscan.io/tx/0xf4e9c9899da7d083f260fd8d0d326a6a0e965f03444a32c73e30cd30ccc609f7 + EXPECT_EQ(result, "{\"callData\":\"0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"21888\",\"initCode\":\"0x5a87209b755781cf65feeedd3855ade0317f4a925fbfb9cf00000000000000000000000078d9c32b96bb872d66d51818227563f44e67e2380000000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"6797860741\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"48152\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x1560b19d17613ec8580cb0feaf7ac2953771404c5bd7830f585e5062e6ddd4b82ae3bb8dbddb659c0300e8009857b5c77501e1cfd5bbab48d03de0ea7207d07c1b\",\"verificationGasLimit\":\"471666\"}"); +} + +TEST(EthereumSigner, EIP4337_NativeTransfer_Account_Deployed) { + const uint256_t chainID = 5; + const auto transaction = UserOperation::buildNativeTransfer( + parse_hex("0x1306b01bC3e4AD202612D3843387e94737673F53"), + parse_hex("0x5A87209b755781cF65fEeEdd3855ade0317f4a92"), + parse_hex("0x21cc27d7db4fa19857a3702653a7a67ee30ca620"), + parse_hex("0x78d9C32b96Bb872D66D51818227563f44e67E238"), + parse_hex("0xce642355Fa553f408C34a2650Ad2F4A1634d033a"), + 0x2386f26fc10000, + 0x1, + true, + 0x9d55, + 0x186a0, + 0x1a339c9e9, + 0xf, + 0xb708 + ); + + const auto serialized = transaction->serialize(chainID); + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto signature = Signer::sign(key, chainID, transaction); + const auto encoded = transaction->encoded(signature, chainID); + const auto result = std::string(encoded.begin(), encoded.end()); + + // https://goerli.etherscan.io/tx/0x707ee622b87a35eb2ffc3762553db8ba0efc5053cfdbeb44a841562df2a7c2bf + EXPECT_EQ(result, "{\"callData\":\"0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"40277\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"1\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0xaed2011e5cf267de495b38ecf86ad6f1d4c05217a99e59f47e8d52ba3d41c10144785893fa3e7c116a054999e3902fc2771064d0545148bc49f6d7c827fc7a9a1c\",\"verificationGasLimit\":\"100000\"}"); +} + +TEST(EthereumSigner, EIP4337_ERC20_Transfer_Account_Deployed) { + const uint256_t chainID = 5; + const auto transaction = UserOperation::buildERC20Transfer( + parse_hex("0x1306b01bC3e4AD202612D3843387e94737673F53"), + parse_hex("0x5A87209b755781cF65fEeEdd3855ade0317f4a92"), + parse_hex("0x21cc27d7db4fa19857a3702653a7a67ee30ca620"), + parse_hex("0x78d9C32b96Bb872D66D51818227563f44e67E238"), + parse_hex("0x98339d8c260052b7ad81c28c16c0b98420f2b46a"), + parse_hex("0xce642355Fa553f408C34a2650Ad2F4A1634d033a"), + 0x186a0, + 0x6, + true, + 0xf78e, + 0x186a0, + 0x168ad5950f, + 0xf, + 0xbb10 + ); + + const auto serialized = transaction->serialize(chainID); + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto signature = Signer::sign(key, chainID, transaction); + const auto encoded = transaction->encoded(signature, chainID); + const auto result = std::string(encoded.begin(), encoded.end()); + + // https://goerli.etherscan.io/tx/0xe1c45f163cec0b9b2c3fd2307f75e48337d35c7c336ad96675c0adb6ce6fc58f + EXPECT_EQ(result, "{\"callData\":\"0xb61d27f600000000000000000000000098339d8c260052b7ad81c28c16c0b98420f2b46a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"63374\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"6\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"47888\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0xd006c93d6a8753b5e7c1e6349de0dea34eab2e7a533106e0f2e1a3a3b013c8e97b007546dab9d7b8fc471ad14ff2e8aa351dc4f1ecb63bf20f33858dc7366cbe1c\",\"verificationGasLimit\":\"100000\"}"); +} + +TEST(EthereumSigner, EIP4337_ERC20_Approve_Account_Deployed) { + const uint256_t chainID = 5; + const auto transaction = UserOperation::buildERC20Approve( + parse_hex("0x1306b01bC3e4AD202612D3843387e94737673F53"), + parse_hex("0x5A87209b755781cF65fEeEdd3855ade0317f4a92"), + parse_hex("0x21cc27d7db4fa19857a3702653a7a67ee30ca620"), + parse_hex("0x78d9C32b96Bb872D66D51818227563f44e67E238"), + parse_hex("0x98339d8c260052b7ad81c28c16c0b98420f2b46a"), + parse_hex("0xce642355Fa553f408C34a2650Ad2F4A1634d033a"), + 0x186a0, + 0x9, + true, + 0xf78e, + 0x186a0, + 0x168ad5950f, + 0xf, + 0xbb10); + + const auto serialized = transaction->serialize(chainID); + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto signature = Signer::sign(key, chainID, transaction); + const auto encoded = transaction->encoded(signature, chainID); + const auto result = std::string(encoded.begin(), encoded.end()); + + // https://goerli.etherscan.io/tx/0xa267136c0a66534f86b04ef5ba0939e22b547d71ddc2e0ab31018696ef1c916f + EXPECT_EQ(result, "{\"callData\":\"0xb61d27f600000000000000000000000098339d8c260052b7ad81c28c16c0b98420f2b46a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"63374\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"9\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"47888\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x262a67dd8cf3d16a72b7809b3b5ed55e9f4c2b93eedd5a3c6be035fbbd7111164464ec933d0fdfa359e266e318f3ac22702ae428ce14fc142e4475603e6ec15e1c\",\"verificationGasLimit\":\"100000\"}"); +} + +TEST(EthereumSigner, EIP4337_ERC721_Transfer_Account_Deployed) { + const uint256_t chainID = 5; + const auto transaction = UserOperation::buildERC721Transfer( + parse_hex("0x1306b01bC3e4AD202612D3843387e94737673F53"), + parse_hex("0x5A87209b755781cF65fEeEdd3855ade0317f4a92"), + parse_hex("0x21cc27d7db4fa19857a3702653a7a67ee30ca620"), + parse_hex("0x78d9C32b96Bb872D66D51818227563f44e67E238"), + parse_hex("0xf5de760f2e916647fd766b4ad9e85ff943ce3a2b"), + parse_hex("0x8cE23B8769ac01d0df0d5f47Be1A38FeA97F3879"), + parse_hex("0xce642355Fa553f408C34a2650Ad2F4A1634d033a"), + 0x2A8E57, + 12, + true, + 6337400, + 1500000, + 0x168ad5950f, + 0xf, + 49999); + + const auto serialized = transaction->serialize(chainID); + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto signature = Signer::sign(key, chainID, transaction); + const auto encoded = transaction->encoded(signature, chainID); + const auto result = std::string(encoded.begin(), encoded.end()); + + // https://goerli.etherscan.io/tx/0x043f7cf4858c8480c8abbd5a5485bee11c8e70e7c6b87825a78eef00a42b662d + EXPECT_EQ(result, "{\"callData\":\"0xb61d27f6000000000000000000000000f5de760f2e916647fd766b4ad9e85ff943ce3a2b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006423b872dd0000000000000000000000008ce23b8769ac01d0df0d5f47be1a38fea97f3879000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000002a8e5700000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"6337400\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"12\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"49999\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x5951cc161a4d60d6b59503efb93e446f5d1a2e3a41d4503ba6393bcf2a2637340d0a865ed5d4d7650a68cbb95915eaa7ed54fd2c42b4bf7c83376f5c5d70691d1b\",\"verificationGasLimit\":\"1500000\"}"); +} + +TEST(EthereumSigner, EIP4337_ERC1155_Transfer_Account_Deployed) { + const uint256_t chainID = 5; + const auto transaction = UserOperation::buildERC1155Transfer( + parse_hex("0x1306b01bC3e4AD202612D3843387e94737673F53"), + parse_hex("0x76627b8D1E01fAF0C73B69625BC1fCb8FA19a2AD"), + parse_hex("0x510ab68bd111ce7115df797118b0334d727d564b"), + parse_hex("0x78d9C32b96Bb872D66D51818227563f44e67E238"), + parse_hex("0x428ce4b916332e1afccfddce08baecc97cb40b12"), + parse_hex("0x8c560E00680b973645900528EDe71a99b8d4dca8"), + parse_hex("0xce642355Fa553f408C34a2650Ad2F4A1634d033a"), + 0x01, + 0, + {}, + 0, + true, + 6337400, + 1500000, + 0x168ad5950f, + 0xf, + 51000); + + const auto serialized = transaction->serialize(chainID); + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto signature = Signer::sign(key, chainID, transaction); + const auto encoded = transaction->encoded(signature, chainID); + const auto result = std::string(encoded.begin(), encoded.end()); + + // https://goerli.etherscan.io/tx/0x6e9875715f2e46dfb45f6d7ba15dc8bd1561abb3ae7d19e549929835fca5f6af + EXPECT_EQ(result, "{\"callData\":\"0xb61d27f6000000000000000000000000428ce4b916332e1afccfddce08baecc97cb40b120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c4f242432a0000000000000000000000008c560e00680b973645900528ede71a99b8d4dca8000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"6337400\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"51000\",\"sender\":\"0x8c560e00680b973645900528ede71a99b8d4dca8\",\"signature\":\"0xaae38bcf9f946921541b44c2a66596968beecb9420471e2c9c531f758a2d652930ffdeeab95742e57e8520fb5c8ca4fee6a8e47e37336d4201fe104103f85e111c\",\"verificationGasLimit\":\"1500000\"}"); +} + +TEST(EthereumSigner, SignatureBreakdownNoEip155) { + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto hash = parse_hex("0xf86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080"); + const auto signature = Signer::sign(key, hash, false, 5); + + const auto r = store(signature.r); + EXPECT_EQ(hex(r), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); + + const auto v = store(signature.v); + EXPECT_EQ(hex(v), "00"); + + const auto s = store(signature.s); + EXPECT_EQ(hex(s), "786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); + + const auto converted = Signer::simpleStructToSignatureData(signature); + EXPECT_EQ(hex(converted), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a00"); +} + +TEST(EthereumSigner, SignatureBreakdownEip155Legacy) { + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto hash = parse_hex("0xf86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080"); + const auto signature = Signer::sign(key, hash, true, 0); + + const auto r = store(signature.r); + EXPECT_EQ(hex(r), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); + + const auto v = store(signature.v); + EXPECT_EQ(hex(v), "1b"); + + const auto s = store(signature.s); + EXPECT_EQ(hex(s), "786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); + + const auto converted = Signer::simpleStructToSignatureData(signature); + EXPECT_EQ(hex(converted), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a1b"); +} + +TEST(EthereumSigner, SignatureBreakdownEip155) { + const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); + const auto hash = parse_hex("0xf86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080"); + const auto signature = Signer::sign(key, hash, true, 1); + + const auto r = store(signature.r); + EXPECT_EQ(hex(r), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); + + const auto v = store(signature.v); + EXPECT_EQ(hex(v), "25"); + + const auto s = store(signature.s); + EXPECT_EQ(hex(s), "786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); + + const auto converted = Signer::simpleStructToSignatureData(signature); + EXPECT_EQ(hex(converted), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a25"); +} + } // namespace TW::Ethereum diff --git a/tests/chains/Ethereum/TWAnySignerTests.cpp b/tests/chains/Ethereum/TWAnySignerTests.cpp index bf4079e3f77..40c50427be3 100644 --- a/tests/chains/Ethereum/TWAnySignerTests.cpp +++ b/tests/chains/Ethereum/TWAnySignerTests.cpp @@ -297,6 +297,321 @@ TEST(TWAnySignerEthereum, SignERC1155Transfer) { ASSERT_EQ(hex(output.data()), "f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000"); } +// EIP4337 + +TEST(TWAnySignerEthereum, EIP4337_SignTransferAccountNotDeployed) { + // https://goerli.etherscan.io/tx/0xf4e9c9899da7d083f260fd8d0d326a6a0e965f03444a32c73e30cd30ccc609f7 + Proto::SigningInput input; + auto chainId = store(uint256_t(5)); + auto nonce = store(uint256_t(0)); + auto amount = store(uint256_t(0x2386f26fc10000)); + auto gasLimit = store(uint256_t(0x5580)); + auto verificationGasLimit = store(uint256_t(0x073272)); + auto maxFeePerGas = store(uint256_t(0x01952f1f85)); + auto maxInclusionFeePerGas = store(uint256_t(0x0f)); + auto preVerificationGas = store(uint256_t(0xbc18)); + auto entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53"; + auto factory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92"; + auto logic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620"; + auto owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238"; + auto to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a"; + + auto key = parse_hex("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::UserOp); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_to_address(to); + + auto& user_operation = *input.mutable_user_operation(); + user_operation.set_verification_gas_limit(verificationGasLimit.data(), verificationGasLimit.size()); + user_operation.set_pre_verification_gas(preVerificationGas.data(), preVerificationGas.size()); + user_operation.set_is_account_deployed(false); + user_operation.set_entry_point(entryPoint); + user_operation.set_account_factory(factory); + user_operation.set_account_logic(logic); + user_operation.set_owner(owner); + + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_transfer(); + transfer.set_amount(amount.data(), amount.size()); + + std::string expected = "{\"callData\":\"0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"21888\",\"initCode\":\"0x5a87209b755781cf65feeedd3855ade0317f4a925fbfb9cf00000000000000000000000078d9c32b96bb872d66d51818227563f44e67e2380000000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"6797860741\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"48152\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x1560b19d17613ec8580cb0feaf7ac2953771404c5bd7830f585e5062e6ddd4b82ae3bb8dbddb659c0300e8009857b5c77501e1cfd5bbab48d03de0ea7207d07c1b\",\"verificationGasLimit\":\"471666\"}"; + { + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(std::string(output.encoded()), expected); + } +} + +TEST(TWAnySignerEthereum, EIP4337_SignTransferAccountDeployed) { + // https://goerli.etherscan.io/tx/0x707ee622b87a35eb2ffc3762553db8ba0efc5053cfdbeb44a841562df2a7c2bf + Proto::SigningInput input; + auto chainId = store(uint256_t(5)); + auto nonce = store(uint256_t(1)); + auto amount = store(uint256_t(0x2386f26fc10000)); + auto gasLimit = store(uint256_t(0x9d55)); + auto verificationGasLimit = store(uint256_t(0x186a0)); + auto maxFeePerGas = store(uint256_t(0x1a339c9e9)); + auto maxInclusionFeePerGas = store(uint256_t(0xf)); + auto preVerificationGas = store(uint256_t(0xb708)); + auto entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53"; + auto factory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92"; + auto logic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620"; + auto owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238"; + auto to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a"; + + auto key = parse_hex("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::UserOp); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_to_address(to); + + auto& user_operation = *input.mutable_user_operation(); + user_operation.set_verification_gas_limit(verificationGasLimit.data(), verificationGasLimit.size()); + user_operation.set_pre_verification_gas(preVerificationGas.data(), preVerificationGas.size()); + user_operation.set_is_account_deployed(true); + user_operation.set_entry_point(entryPoint); + user_operation.set_account_factory(factory); + user_operation.set_account_logic(logic); + user_operation.set_owner(owner); + + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_transfer(); + transfer.set_amount(amount.data(), amount.size()); + + std::string expected = "{\"callData\":\"0xb61d27f6000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"40277\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"1\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0xaed2011e5cf267de495b38ecf86ad6f1d4c05217a99e59f47e8d52ba3d41c10144785893fa3e7c116a054999e3902fc2771064d0545148bc49f6d7c827fc7a9a1c\",\"verificationGasLimit\":\"100000\"}"; + + { + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(std::string(output.encoded()), expected); + } +} + +TEST(TWAnySignerEthereum, EIP4337_SignERC20TransferAccountDeployed) { + // https://goerli.etherscan.io/tx/0xe1c45f163cec0b9b2c3fd2307f75e48337d35c7c336ad96675c0adb6ce6fc58f + Proto::SigningInput input; + auto chainId = store(uint256_t(5)); + auto nonce = store(uint256_t(6)); + auto amount = store(uint256_t(0x186a0)); + auto gasLimit = store(uint256_t(0xf78e)); + auto verificationGasLimit = store(uint256_t(0x186a0)); + auto maxFeePerGas = store(uint256_t(0x168ad5950f)); + auto maxInclusionFeePerGas = store(uint256_t(0xf)); + auto preVerificationGas = store(uint256_t(0xbb10)); + auto entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53"; + auto factory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92"; + auto logic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620"; + auto owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238"; + auto tokenContract = "0x98339d8c260052b7ad81c28c16c0b98420f2b46a"; + auto to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a"; + + auto key = parse_hex("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::UserOp); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_to_address(tokenContract); + + auto& user_operation = *input.mutable_user_operation(); + user_operation.set_verification_gas_limit(verificationGasLimit.data(), verificationGasLimit.size()); + user_operation.set_pre_verification_gas(preVerificationGas.data(), preVerificationGas.size()); + user_operation.set_is_account_deployed(true); + user_operation.set_entry_point(entryPoint); + user_operation.set_account_factory(factory); + user_operation.set_account_logic(logic); + user_operation.set_owner(owner); + + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_erc20_transfer(); + transfer.set_amount(amount.data(), amount.size()); + transfer.set_to(to); + + std::string expected = "{\"callData\":\"0xb61d27f600000000000000000000000098339d8c260052b7ad81c28c16c0b98420f2b46a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"63374\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"6\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"47888\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0xd006c93d6a8753b5e7c1e6349de0dea34eab2e7a533106e0f2e1a3a3b013c8e97b007546dab9d7b8fc471ad14ff2e8aa351dc4f1ecb63bf20f33858dc7366cbe1c\",\"verificationGasLimit\":\"100000\"}"; + { + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(std::string(output.encoded()), expected); + } +} + +TEST(TWAnySignerEthereum, EIP4337_SignERC20ApproveAccountDeployed) { + // https://goerli.etherscan.io/tx/0xe1c45f163cec0b9b2c3fd2307f75e48337d35c7c336ad96675c0adb6ce6fc58f + Proto::SigningInput input; + auto chainId = store(uint256_t(5)); + auto nonce = store(uint256_t(9)); + auto amount = store(uint256_t(0x186a0)); + auto gasLimit = store(uint256_t(0xf78e)); + auto verificationGasLimit = store(uint256_t(0x186a0)); + auto maxFeePerGas = store(uint256_t(0x168ad5950f)); + auto maxInclusionFeePerGas = store(uint256_t(0xf)); + auto preVerificationGas = store(uint256_t(0xbb10)); + auto entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53"; + auto factory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92"; + auto logic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620"; + auto owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238"; + auto tokenContract = "0x98339d8c260052b7ad81c28c16c0b98420f2b46a"; + auto to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a"; + + auto key = parse_hex("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::UserOp); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_to_address(tokenContract); + + auto& user_operation = *input.mutable_user_operation(); + user_operation.set_verification_gas_limit(verificationGasLimit.data(), verificationGasLimit.size()); + user_operation.set_pre_verification_gas(preVerificationGas.data(), preVerificationGas.size()); + user_operation.set_is_account_deployed(true); + user_operation.set_entry_point(entryPoint); + user_operation.set_account_factory(factory); + user_operation.set_account_logic(logic); + user_operation.set_owner(owner); + + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_erc20_approve(); + transfer.set_amount(amount.data(), amount.size()); + transfer.set_spender(to); + + std::string expected = "{\"callData\":\"0xb61d27f600000000000000000000000098339d8c260052b7ad81c28c16c0b98420f2b46a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"63374\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"9\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"47888\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x262a67dd8cf3d16a72b7809b3b5ed55e9f4c2b93eedd5a3c6be035fbbd7111164464ec933d0fdfa359e266e318f3ac22702ae428ce14fc142e4475603e6ec15e1c\",\"verificationGasLimit\":\"100000\"}"; + { + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(std::string(output.encoded()), expected); + } +} + +TEST(TWAnySignerEthereum, EIP4337_SignERC721TransferAccountDeployed) { + // https://goerli.etherscan.io/tx/0x043f7cf4858c8480c8abbd5a5485bee11c8e70e7c6b87825a78eef00a42b662d + Proto::SigningInput input; + auto chainId = store(uint256_t(5)); + auto nonce = store(uint256_t(12)); + auto tokenId = store(uint256_t(0x2A8E57)); + auto gasLimit = store(uint256_t(6337400)); + auto verificationGasLimit = store(uint256_t(1500000)); + auto maxFeePerGas = store(uint256_t(0x168ad5950f)); + auto maxInclusionFeePerGas = store(uint256_t(0xf)); + auto preVerificationGas = store(uint256_t(49999)); + auto entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53"; + auto factory = "0x5A87209b755781cF65fEeEdd3855ade0317f4a92"; + auto logic = "0x21cc27d7db4fa19857a3702653a7a67ee30ca620"; + auto owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238"; + auto tokenContract = "0xf5de760f2e916647fd766b4ad9e85ff943ce3a2b"; + auto to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a"; + auto from = "0x8cE23B8769ac01d0df0d5f47Be1A38FeA97F3879"; + + auto key = parse_hex("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::UserOp); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_to_address(tokenContract); + + auto& user_operation = *input.mutable_user_operation(); + user_operation.set_verification_gas_limit(verificationGasLimit.data(), verificationGasLimit.size()); + user_operation.set_pre_verification_gas(preVerificationGas.data(), preVerificationGas.size()); + user_operation.set_is_account_deployed(true); + user_operation.set_entry_point(entryPoint); + user_operation.set_account_factory(factory); + user_operation.set_account_logic(logic); + user_operation.set_owner(owner); + + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_erc721_transfer(); + transfer.set_token_id(tokenId.data(), tokenId.size()); + transfer.set_to(to); + transfer.set_from(from); + + std::string expected = "{\"callData\":\"0xb61d27f6000000000000000000000000f5de760f2e916647fd766b4ad9e85ff943ce3a2b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006423b872dd0000000000000000000000008ce23b8769ac01d0df0d5f47be1a38fea97f3879000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a00000000000000000000000000000000000000000000000000000000002a8e5700000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"6337400\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"12\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"49999\",\"sender\":\"0x8ce23b8769ac01d0df0d5f47be1a38fea97f3879\",\"signature\":\"0x5951cc161a4d60d6b59503efb93e446f5d1a2e3a41d4503ba6393bcf2a2637340d0a865ed5d4d7650a68cbb95915eaa7ed54fd2c42b4bf7c83376f5c5d70691d1b\",\"verificationGasLimit\":\"1500000\"}"; + { + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(std::string(output.encoded()), expected); + } +} + +TEST(TWAnySignerEthereum, EIP4337_SignERC1155TransferAccountDeployed) { + // https://goerli.etherscan.io/tx/0x6e9875715f2e46dfb45f6d7ba15dc8bd1561abb3ae7d19e549929835fca5f6af + Proto::SigningInput input; + auto chainId = store(uint256_t(5)); + auto nonce = store(uint256_t(0)); + auto tokenId = store(uint256_t(0x01)); + auto gasLimit = store(uint256_t(6337400)); + auto verificationGasLimit = store(uint256_t(1500000)); + auto maxFeePerGas = store(uint256_t(0x168ad5950f)); + auto maxInclusionFeePerGas = store(uint256_t(0xf)); + auto preVerificationGas = store(uint256_t(51000)); + auto entryPoint = "0x1306b01bC3e4AD202612D3843387e94737673F53"; + auto factory = "0x76627b8D1E01fAF0C73B69625BC1fCb8FA19a2AD"; + auto logic = "0x510ab68bd111ce7115df797118b0334d727d564b"; + auto owner = "0x78d9C32b96Bb872D66D51818227563f44e67E238"; + auto tokenContract = "0x428ce4b916332e1afccfddce08baecc97cb40b12"; + auto to = "0xce642355Fa553f408C34a2650Ad2F4A1634d033a"; + auto from = "0x8c560E00680b973645900528EDe71a99b8d4dca8"; + + auto key = parse_hex("0xf9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::UserOp); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_to_address(tokenContract); + + auto& user_operation = *input.mutable_user_operation(); + user_operation.set_verification_gas_limit(verificationGasLimit.data(), verificationGasLimit.size()); + user_operation.set_pre_verification_gas(preVerificationGas.data(), preVerificationGas.size()); + user_operation.set_is_account_deployed(true); + user_operation.set_entry_point(entryPoint); + user_operation.set_account_factory(factory); + user_operation.set_account_logic(logic); + user_operation.set_owner(owner); + + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_erc1155_transfer(); + transfer.set_token_id(tokenId.data(), tokenId.size()); + transfer.set_to(to); + transfer.set_from(from); + + std::string expected = "{\"callData\":\"0xb61d27f6000000000000000000000000428ce4b916332e1afccfddce08baecc97cb40b120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c4f242432a0000000000000000000000008c560e00680b973645900528ede71a99b8d4dca8000000000000000000000000ce642355fa553f408c34a2650ad2f4a1634d033a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"6337400\",\"initCode\":\"0x\",\"maxFeePerGas\":\"96818533647\",\"maxPriorityFeePerGas\":\"15\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"51000\",\"sender\":\"0x8c560e00680b973645900528ede71a99b8d4dca8\",\"signature\":\"0xaae38bcf9f946921541b44c2a66596968beecb9420471e2c9c531f758a2d652930ffdeeab95742e57e8520fb5c8ca4fee6a8e47e37336d4201fe104103f85e111c\",\"verificationGasLimit\":\"1500000\"}"; + { + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(std::string(output.encoded()), expected); + } +} + TEST(TWAnySignerEthereum, SignJSON) { auto json = STRING(R"({"chainId":"AQ==","gasPrice":"1pOkAA==","gasLimit":"Ugg=","toAddress":"0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1","transaction":{"transfer":{"amount":"A0i8paFgAA=="}}})"); auto key = DATA("17209af590a86462395d5881e60d11c7fa7d482cfb02b5a01b93c2eeef243543"); From 3deeae2f308a8de85b3147a9b263441df517c703 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 6 Feb 2023 11:33:09 +0100 Subject: [PATCH 093/426] feat(fastlane): temporary disable mac build (#2911) --- swift/fastlane/Fastfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/fastlane/Fastfile b/swift/fastlane/Fastfile index 298bcb35a56..5fa18ea4945 100644 --- a/swift/fastlane/Fastfile +++ b/swift/fastlane/Fastfile @@ -13,7 +13,7 @@ platform :ios do create_xcframework( workspace: 'TrustWalletCore.xcworkspace', scheme: 'WalletCore', - destinations: ['iOS', 'macOS'], + destinations: ['iOS'], xcframework_output_directory: 'build', enable_bitcode: false ) @@ -24,7 +24,7 @@ platform :ios do create_xcframework( workspace: 'TrustWalletCore.xcworkspace', scheme: 'SwiftProtobuf', - destinations: ['iOS', 'macOS'], + destinations: ['iOS'], xcframework_output_directory: 'build', enable_bitcode: false ) From d8ddf7b8102a0ae505ced10b3ccdcb884fedeef7 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 7 Feb 2023 06:32:54 +0100 Subject: [PATCH 094/426] Re-enable rust build for mac catalyst (#2913) --- .github/workflows/linux-ci-sonarcloud.yml | 2 +- .github/workflows/linux-ci.yml | 2 +- .github/workflows/linux-sampleapp-ci.yml | 2 +- .github/workflows/wasm-ci.yml | 2 +- swift/common-xcframework.yml | 2 +- swift/project.yml | 2 +- tools/ios-build | 3 +- tools/rust-bindgen | 77 +++++++++++++++++++++++ 8 files changed, 85 insertions(+), 7 deletions(-) diff --git a/.github/workflows/linux-ci-sonarcloud.yml b/.github/workflows/linux-ci-sonarcloud.yml index 8941656e9b5..dd1829a065c 100644 --- a/.github/workflows/linux-ci-sonarcloud.yml +++ b/.github/workflows/linux-ci-sonarcloud.yml @@ -9,7 +9,7 @@ on: jobs: build: if: github.event.pull_request.head.repo.fork == false - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install system dependencies diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 5bef8727e51..944ee976432 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install system dependencies diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index 7ed0fdcbbb0..c963437f5d3 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install system dependencies diff --git a/.github/workflows/wasm-ci.yml b/.github/workflows/wasm-ci.yml index d41158c4933..3eba0a86a70 100644 --- a/.github/workflows/wasm-ci.yml +++ b/.github/workflows/wasm-ci.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/swift/common-xcframework.yml b/swift/common-xcframework.yml index d556db5dcdc..dae0d4b55c7 100644 --- a/swift/common-xcframework.yml +++ b/swift/common-xcframework.yml @@ -45,7 +45,7 @@ targets: - framework: WalletCoreRs.xcframework settings: SKIP_INSTALL: false - SUPPORTS_MACCATALYST: false + SUPPORTS_MACCATALYST: true INFOPLIST_FILE: 'Info.plist' CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION: YES_ERROR CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER: $(inherited) false diff --git a/swift/project.yml b/swift/project.yml index 69afe8dff90..df46f6a29e5 100644 --- a/swift/project.yml +++ b/swift/project.yml @@ -45,7 +45,7 @@ targets: gatherCoverageData: true settings: SKIP_INSTALL: false - SUPPORTS_MACCATALYST: false + SUPPORTS_MACCATALYST: true DEBUG_INFORMATION_FORMAT: dwarf-with-dsym BUILD_LIBRARY_FOR_DISTRIBUTION: true INFOPLIST_FILE: 'Info.plist' diff --git a/tools/ios-build b/tools/ios-build index ace228be684..c7ddc0e5721 100755 --- a/tools/ios-build +++ b/tools/ios-build @@ -47,13 +47,14 @@ create_xc_framework() { xcodebuild -create-xcframework -output $BUILD_FOLDER/$FRAMEWORK.xcframework \ -framework $BUILD_FOLDER/ios-arm64.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ -framework $BUILD_FOLDER/ios-arm64_x86_64-simulator.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ + -framework $BUILD_FOLDER/ios-x86_64_arm64-maccatalyst.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ -framework $BUILD_FOLDER/macos-arm64_x86_64.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework } main() { init build_mac_x64_arm64 - #build_ios_mac_catalyst + build_ios_mac_catalyst build_ios_arm64 && build_ios_simulator create_xc_framework } diff --git a/tools/rust-bindgen b/tools/rust-bindgen index c866a43b2ee..044e16faf6a 100755 --- a/tools/rust-bindgen +++ b/tools/rust-bindgen @@ -11,6 +11,8 @@ HEADER_NAME="WalletCoreRSBindgen.h" create_xc_framework() { rm -rf $TARGET_XCFRAMEWORK_NAME xcodebuild -create-xcframework -library $BUILD_FOLDER/$TARGET_NAME -library $BUILD_FOLDER/darwin_universal/$TARGET_NAME -library $BUILD_FOLDER/aarch64-apple-ios/release/$TARGET_NAME -output $TARGET_XCFRAMEWORK_NAME + mkdir -p $TARGET_XCFRAMEWORK_NAME/ios-arm64_x86_64-maccatalyst + cp $BUILD_FOLDER/catalyst/$TARGET_NAME $TARGET_XCFRAMEWORK_NAME/ios-arm64_x86_64-maccatalyst } cd rust @@ -43,3 +45,78 @@ fi cbindgen --crate $CRATE --output ../src/rust/bindgen/$HEADER_NAME cd - cp build/local/release/${TARGET_NAME} build/local/lib/ + +if [[ `uname` == "Darwin" ]]; then +cd rust +cat > $TARGET_XCFRAMEWORK_NAME/Info.plist << EOF + + + + + AvailableLibraries + + + LibraryIdentifier + macos-arm64_x86_64 + LibraryPath + libwallet_core_rs.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + macos + + + LibraryIdentifier + ios-arm64_x86_64-maccatalyst + LibraryPath + libwallet_core_rs.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + maccatalyst + + + LibraryIdentifier + ios-arm64 + LibraryPath + libwallet_core_rs.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + libwallet_core_rs.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + +EOF +cd - +fi From 074c93e583714268da84f716607c0514973e3552 Mon Sep 17 00:00:00 2001 From: Ruslan Serebriakov Date: Tue, 7 Feb 2023 05:33:51 +0000 Subject: [PATCH 095/426] Some post-PR cleanups (#2914) --- .../ethereum/TestEthereumAddress.kt | 9 ------- include/TrustWalletCore/TWEthereum.h | 10 ------- src/Ethereum/EIP1014.cpp | 5 ---- src/Ethereum/EIP1014.h | 1 - src/Ethereum/EIP4337.cpp | 2 +- src/Ethereum/Transaction.cpp | 4 +-- src/interface/TWEthereum.cpp | 7 ----- swift/Tests/Blockchains/EthereumTests.swift | 8 ------ tests/chains/Ethereum/EIP1014Tests.cpp | 26 ------------------- 9 files changed, 3 insertions(+), 69 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt index 3089d9bd510..9c3ec88fd7d 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAddress.kt @@ -15,15 +15,6 @@ class TestEthereumAddress { System.loadLibrary("TrustWalletCore") } - @Test - fun testEthereumCreate2Addresses() { - val from = "0x0000000000000000000000000000000000000000" - val salt = Numeric.hexStringToByteArray("0x0000000000000000000000000000000000000000000000000000000000000000") - val initCodeHash = Hash.keccak256(Numeric.hexStringToByteArray("0x0")) - val result = Ethereum.eip1014AddressCreate2(from, salt, initCodeHash) - assertEquals(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38") - } - @Test fun testEthereumAddresses() { val any = AnyAddress("0x7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1", CoinType.ETHEREUM) diff --git a/include/TrustWalletCore/TWEthereum.h b/include/TrustWalletCore/TWEthereum.h index 9fab0ddba90..b65a9281a97 100644 --- a/include/TrustWalletCore/TWEthereum.h +++ b/include/TrustWalletCore/TWEthereum.h @@ -7,7 +7,6 @@ #pragma once #include "TWBase.h" -#include "TWData.h" #include "TWString.h" TW_EXTERN_C_BEGIN @@ -15,15 +14,6 @@ TW_EXTERN_C_BEGIN TW_EXPORT_STRUCT struct TWEthereum; -/// EIP-1014: Skinny CREATE2 (guess smart contract create2 address) -/// -/// \param fromEthAddress valid eth address -/// \param salt always 32 bytes stack item -/// \param initCodeHash The init_code is the code that, when executed, produces the runtime bytecode that will be placed into the state, and which typically is used by high level languages to implement a ‘constructor’. Need to be provided hashed with keccak256 -/// \return Ethereum resulting address -TW_EXPORT_STATIC_METHOD -TWString* _Nonnull TWEthereumEip1014AddressCreate2(TWString* _Nonnull fromEthAddress, TWData* _Nonnull salt, TWData* _Nonnull initCodeHash); - /// Generate a layer 2 eip2645 derivation path from eth address, layer, application and given index. /// /// \param wallet non-null TWHDWallet diff --git a/src/Ethereum/EIP1014.cpp b/src/Ethereum/EIP1014.cpp index 4242126a8d1..dfc3be3efae 100644 --- a/src/Ethereum/EIP1014.cpp +++ b/src/Ethereum/EIP1014.cpp @@ -26,9 +26,4 @@ Data create2Address(const std::string& from, const Data& salt, const Data& initC return Data(hash.end() - 20, hash.end()); } -std::string create2AddressString(const std::string& from, const Data& salt, const Data& initCodeHash) { - auto addressData = create2Address(from, salt, initCodeHash); - return Ethereum::checksumed(Address(hexEncoded(addressData))); -} - } // namespace TW::Ethereum diff --git a/src/Ethereum/EIP1014.h b/src/Ethereum/EIP1014.h index d1bde394e22..3356fe926e8 100644 --- a/src/Ethereum/EIP1014.h +++ b/src/Ethereum/EIP1014.h @@ -11,6 +11,5 @@ namespace TW::Ethereum { Data create2Address(const std::string& from, const Data& salt, const Data& initCodeHash); -std::string create2AddressString(const std::string& from, const Data& salt, const Data& initCodeHash); } diff --git a/src/Ethereum/EIP4337.cpp b/src/Ethereum/EIP4337.cpp index e377f996c24..f80e9d59c27 100644 --- a/src/Ethereum/EIP4337.cpp +++ b/src/Ethereum/EIP4337.cpp @@ -57,7 +57,7 @@ std::string getEIP4337DeploymentAddress(const std::string& factoryAddress, const const Data proxyInitCode = getEIP1967ProxyInitCode(logicAddress, logicInitializeBytecode); const Data salt = parse_hex("0x0000000000000000000000000000000000000000000000000000000000000000"); const Data initCodeHash = Hash::keccak256(proxyInitCode); - return create2AddressString(factoryAddress, salt, initCodeHash); + return Ethereum::checksumed(Address(hexEncoded(create2Address(factoryAddress, salt, initCodeHash)))); } } // namespace TW::Ethereum diff --git a/src/Ethereum/Transaction.cpp b/src/Ethereum/Transaction.cpp index bb168b91cee..f81415dab3d 100644 --- a/src/Ethereum/Transaction.cpp +++ b/src/Ethereum/Transaction.cpp @@ -234,7 +234,7 @@ Data UserOperation::preHash(const uint256_t chainID) const { return MessageSigner::generateMessage(hashStr); } -Data UserOperation::serialize(const uint256_t chainID) const { +Data UserOperation::serialize([[maybe_unused]] const uint256_t chainID) const { auto params = ABI::ParamTuple(ParamCollection{ std::make_shared(sender), std::make_shared(nonce), @@ -253,7 +253,7 @@ Data UserOperation::serialize(const uint256_t chainID) const { return Data(serialized.begin(), serialized.end() - 32); // remove trailing word (zero-length signature) } -Data UserOperation::encoded(const Signature& signature, const uint256_t chainID) const { +Data UserOperation::encoded(const Signature& signature, [[maybe_unused]] const uint256_t chainID) const { Data rawSignature = Signer::simpleStructToSignatureData(signature); rawSignature[64] += 27; diff --git a/src/interface/TWEthereum.cpp b/src/interface/TWEthereum.cpp index ee8c42a7b85..4e9440268d5 100644 --- a/src/interface/TWEthereum.cpp +++ b/src/interface/TWEthereum.cpp @@ -12,13 +12,6 @@ #include -TWString* TWEthereumEip1014AddressCreate2(TWString* _Nonnull fromEthAddress, TWData* _Nonnull salt, TWData* _Nonnull initCodeHash) { - const auto& ethAddressStr = *reinterpret_cast(fromEthAddress); - const auto& saltData = *reinterpret_cast(salt); - const auto& initCodeHashData = *reinterpret_cast(initCodeHash); - return new std::string(TW::Ethereum::create2AddressString(ethAddressStr, saltData, initCodeHashData)); -} - TWString* TWEthereumEip2645GetPath(TWString* ethAddress, TWString* layer, TWString* application, TWString* index) { const auto& ethAddressStr = *reinterpret_cast(ethAddress); const auto& layerStr = *reinterpret_cast(layer); diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index 8a67d5e9756..fc6e0e6dd4d 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -374,14 +374,6 @@ class EthereumTests: XCTestCase { // EIP4337 - func testCreate2Address() { - let address = "0x0000000000000000000000000000000000000000" - let salt = Data(hexString: "0x0000000000000000000000000000000000000000000000000000000000000000")! - let initCodeHash = Hash.keccak256(data: Data(hexString: "0x00")!) - let result = Ethereum.eip1014AddressCreate2(fromEthAddress: address, salt: salt, initCodeHash: initCodeHash) - XCTAssertEqual(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38") - } - func testEIP4337DeploymentAddress() { let factoryAddress = "0xd9145CCE52D386f254917e481eB44e9943F39138" let logicAddress = "0x5C9eb5D6a6C2c1B3EFc52255C0b356f116f6f66D" diff --git a/tests/chains/Ethereum/EIP1014Tests.cpp b/tests/chains/Ethereum/EIP1014Tests.cpp index dab96107ebc..a13b9764090 100644 --- a/tests/chains/Ethereum/EIP1014Tests.cpp +++ b/tests/chains/Ethereum/EIP1014Tests.cpp @@ -23,16 +23,6 @@ namespace TW::Ethereum::tests { Data initCodeHash = Hash::keccak256(parse_hex("0x00")); const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"); - } - - // C - { - const auto from = STRING("0x0000000000000000000000000000000000000000"); - const auto salt = DATA("0x0000000000000000000000000000000000000000000000000000000000000000"); - const auto initCodeHash = WRAPD(TWHashKeccak256(DATA("0x00").get())); - const auto& result = WRAPS(TWEthereumEip1014AddressCreate2(from.get(), salt.get(), initCodeHash.get())); - assertStringsEqual(result, "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"); } } @@ -44,16 +34,6 @@ namespace TW::Ethereum::tests { Data initCodeHash = Hash::keccak256(parse_hex("0x00")); const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3"); - } - - // C - { - const auto from = STRING("0xdeadbeef00000000000000000000000000000000"); - const auto salt = DATA("0x0000000000000000000000000000000000000000000000000000000000000000"); - const auto initCodeHash = WRAPD(TWHashKeccak256(DATA("0x00").get())); - const auto& result = WRAPS(TWEthereumEip1014AddressCreate2(from.get(), salt.get(), initCodeHash.get())); - assertStringsEqual(result, "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3"); } } @@ -64,7 +44,6 @@ namespace TW::Ethereum::tests { initCode.resize(32); const auto& addressData = Ethereum::create2Address(from, salt, initCode); ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x2DB27D1d6BE32C9abfA484BA3d591101881D4B9f"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCode), "0x2DB27D1d6BE32C9abfA484BA3d591101881D4B9f"); } TEST(EthereumEip1014, Example3) { @@ -74,7 +53,6 @@ namespace TW::Ethereum::tests { initCode.resize(32); const auto& addressData = Ethereum::create2Address(from, salt, initCode); ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x219438aC82230Cb9A9C13Cd99D324fA1d66CF018"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCode), "0x219438aC82230Cb9A9C13Cd99D324fA1d66CF018"); } TEST(EthereumEip1014, Example4) { @@ -83,7 +61,6 @@ namespace TW::Ethereum::tests { Data initCodeHash = Hash::keccak256(parse_hex("0xdeadbeef")); const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7"); } TEST(EthereumEip1014, Example5) { @@ -92,7 +69,6 @@ namespace TW::Ethereum::tests { Data initCodeHash = Hash::keccak256(parse_hex("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C"); } TEST(EthereumEip1014, Example6) { @@ -101,7 +77,6 @@ namespace TW::Ethereum::tests { Data initCodeHash = Hash::keccak256(parse_hex("0x")); const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0"); } TEST(EthereumEip1014, Example7) { @@ -110,6 +85,5 @@ namespace TW::Ethereum::tests { Data initCodeHash = Hash::keccak256(parse_hex("0x608060405260405162000c5138038062000c51833981810160405281019062000029919062000580565b6200003d828260006200004560201b60201c565b5050620007d7565b62000056836200008860201b60201c565b600082511180620000645750805b156200008357620000818383620000df60201b620000371760201c565b505b505050565b62000099816200011560201b60201c565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b60606200010d838360405180606001604052806027815260200162000c2a60279139620001eb60201b60201c565b905092915050565b6200012b816200027d60201b620000641760201c565b6200016d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000164906200066d565b60405180910390fd5b80620001a77f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b620002a060201b620000871760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60606000808573ffffffffffffffffffffffffffffffffffffffff1685604051620002179190620006dc565b600060405180830381855af49150503d806000811462000254576040519150601f19603f3d011682016040523d82523d6000602084013e62000259565b606091505b50915091506200027286838387620002aa60201b60201c565b925050509392505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b606083156200031a5760008351036200031157620002ce856200027d60201b60201c565b62000310576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620003079062000745565b60405180910390fd5b5b8290506200032d565b6200032c83836200033560201b60201c565b5b949350505050565b600082511115620003495781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200037f9190620007b3565b60405180910390fd5b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620003c9826200039c565b9050919050565b620003db81620003bc565b8114620003e757600080fd5b50565b600081519050620003fb81620003d0565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000456826200040b565b810181811067ffffffffffffffff821117156200047857620004776200041c565b5b80604052505050565b60006200048d62000388565b90506200049b82826200044b565b919050565b600067ffffffffffffffff821115620004be57620004bd6200041c565b5b620004c9826200040b565b9050602081019050919050565b60005b83811015620004f6578082015181840152602081019050620004d9565b60008484015250505050565b6000620005196200051384620004a0565b62000481565b90508281526020810184848401111562000538576200053762000406565b5b62000545848285620004d6565b509392505050565b600082601f83011262000565576200056462000401565b5b81516200057784826020860162000502565b91505092915050565b600080604083850312156200059a576200059962000392565b5b6000620005aa85828601620003ea565b925050602083015167ffffffffffffffff811115620005ce57620005cd62000397565b5b620005dc858286016200054d565b9150509250929050565b600082825260208201905092915050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b600062000655602d83620005e6565b91506200066282620005f7565b604082019050919050565b60006020820190508181036000830152620006888162000646565b9050919050565b600081519050919050565b600081905092915050565b6000620006b2826200068f565b620006be81856200069a565b9350620006d0818560208601620004d6565b80840191505092915050565b6000620006ea8284620006a5565b915081905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006200072d601d83620005e6565b91506200073a82620006f5565b602082019050919050565b6000602082019050818103600083015262000760816200071e565b9050919050565b600081519050919050565b60006200077f8262000767565b6200078b8185620005e6565b93506200079d818560208601620004d6565b620007a8816200040b565b840191505092915050565b60006020820190508181036000830152620007cf818462000772565b905092915050565b61044380620007e76000396000f3fe6080604052366100135761001161001d565b005b61001b61001d565b005b610025610091565b610035610030610093565b6100a2565b565b606061005c83836040518060600160405280602781526020016103e7602791396100c8565b905092915050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b565b600061009d61014e565b905090565b3660008037600080366000845af43d6000803e80600081146100c3573d6000f35b3d6000fd5b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516100f291906102db565b600060405180830381855af49150503d806000811461012d576040519150601f19603f3d011682016040523d82523d6000602084013e610132565b606091505b5091509150610143868383876101a5565b925050509392505050565b600061017c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b610087565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606083156102075760008351036101ff576101bf85610064565b6101fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f59061034f565b60405180910390fd5b5b829050610212565b610211838361021a565b5b949350505050565b60008251111561022d5781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161026191906103c4565b60405180910390fd5b600081519050919050565b600081905092915050565b60005b8381101561029e578082015181840152602081019050610283565b60008484015250505050565b60006102b58261026a565b6102bf8185610275565b93506102cf818560208601610280565b80840191505092915050565b60006102e782846102aa565b915081905092915050565b600082825260208201905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b6000610339601d836102f2565b915061034482610303565b602082019050919050565b600060208201905081810360008301526103688161032c565b9050919050565b600081519050919050565b6000601f19601f8301169050919050565b60006103968261036f565b6103a081856102f2565b93506103b0818560208601610280565b6103b98161037a565b840191505092915050565b600060208201905081810360008301526103de818461038b565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e57dd3eafc9985be746025b6d82d4f011b9a7bb3db56f9a1eb7eadfddd376b6064736f6c63430008110033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564000000000000000000000000d9ec9e840bb5df076dbbb488d01485058f421e5800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000be8fa0112dcb7d21dc63645b633073651e19934800000000000000000000000000000000000000000000000000000000")); const auto& addressData = Ethereum::create2Address(from, salt, initCodeHash); ASSERT_EQ(Ethereum::checksumed(Ethereum::Address(hexEncoded(addressData))), "0x4455e5f0038795939c001aa4d296A45956C460AA"); - ASSERT_EQ(Ethereum::create2AddressString(from, salt, initCodeHash), "0x4455e5f0038795939c001aa4d296A45956C460AA"); } } From 66b89fd860d6075d51a2f2530ff772038691063d Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 7 Feb 2023 06:34:29 +0100 Subject: [PATCH 096/426] [ThorChain]: change deposit function to depositWithExpiry (#2912) --- src/THORChain/Swap.cpp | 17 +++++--- src/THORChain/Swap.h | 10 +++++ src/THORChain/TWSwap.cpp | 1 + src/proto/THORChainSwap.proto | 3 ++ tests/chains/THORChain/SwapTests.cpp | 62 +++++++++++++++------------- 5 files changed, 60 insertions(+), 33 deletions(-) diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index 52eb156d7be..4e2d41cabb7 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -256,12 +256,19 @@ SwapBundled SwapBuilder::buildEth(uint256_t amount, const std::string& memo) { input.set_to_address(*mRouterAddress); if (!toTokenId.empty()) { + if (!mExpirationPolicy) { + std::cout << "here"<< std::endl; + auto now = std::chrono::system_clock::now(); + auto in_15_minutes = now + std::chrono::minutes(15); + mExpirationPolicy = std::chrono::duration_cast(in_15_minutes.time_since_epoch()).count(); + } auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); - auto func = Ethereum::ABI::Function("deposit", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(toAssetAddressBin), - std::make_shared(uint256_t(amount)), - std::make_shared(memo)}); + auto func = Ethereum::ABI::Function("depositWithExpiry", std::vector>{ + std::make_shared(vaultAddressBin), + std::make_shared(toAssetAddressBin), + std::make_shared(uint256_t(amount)), + std::make_shared(memo), + std::make_shared(uint256_t(*mExpirationPolicy))}); Data payload; func.encode(payload); transfer.set_data(payload.data(), payload.size()); diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h index 926ef3535b1..54c5e41fceb 100644 --- a/src/THORChain/Swap.h +++ b/src/THORChain/Swap.h @@ -49,6 +49,7 @@ class SwapBuilder { std::optional mAffFeeAddress{std::nullopt}; std::optional mAffFeeRate{std::nullopt}; std::optional mExtraMemo{std::nullopt}; + std::optional mExpirationPolicy{std::nullopt}; SwapBundled buildBitcoin(uint256_t amount, const std::string& memo, Chain fromChain); SwapBundled buildBinance(Proto::Asset fromAsset, uint256_t amount, const std::string& memo); @@ -129,6 +130,15 @@ class SwapBuilder { return *this; } + SwapBuilder& expirationPolicy(std::size_t expirationTime) noexcept { + if (expirationTime > 0) { + mExpirationPolicy = expirationTime; + } else { + mExpirationPolicy = std::nullopt; + } + return *this; + } + std::string buildMemo(bool shortened = true) noexcept; SwapBundled build(bool shortened = true); diff --git a/src/THORChain/TWSwap.cpp b/src/THORChain/TWSwap.cpp index cad48c53aa6..c7da748de0c 100644 --- a/src/THORChain/TWSwap.cpp +++ b/src/THORChain/TWSwap.cpp @@ -36,6 +36,7 @@ TWData* _Nonnull TWTHORChainSwapBuildSwap(TWData* _Nonnull input) { .affFeeAddress(inputProto.affiliate_fee_address()) .affFeeRate(inputProto.affiliate_fee_rate_bp()) .extraMemo(inputProto.extra_memo()) + .expirationPolicy(inputProto.expiration_time()) .build(); outputProto.set_from_chain(fromChain); diff --git a/src/proto/THORChainSwap.proto b/src/proto/THORChainSwap.proto index 0d5d2c248b6..f29a09e3f41 100644 --- a/src/proto/THORChainSwap.proto +++ b/src/proto/THORChainSwap.proto @@ -84,6 +84,9 @@ message SwapInput { // Optional extra custom memo, reserved for later use. string extra_memo = 11; + + // Optional expirationTime, will be now() + 15 min if not set + uint64 expiration_time = 12; } // Result of the swap, a SigningInput struct for the specific chain diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp index 372bfd70d9e..0e793cbc1b1 100644 --- a/tests/chains/THORChain/SwapTests.cpp +++ b/tests/chains/THORChain/SwapTests.cpp @@ -455,66 +455,72 @@ Data SwapTest_ethAddressStringToData(const std::string& asString) { TEST(THORChainSwap, SwapErc20Rune) { Proto::Asset fromAsset; - fromAsset.set_token_id("0xdAC17F958D2ee523a2206206994597C13D831ec7"); - fromAsset.set_chain(static_cast(Chain::ETH)); + fromAsset.set_token_id("0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"); + fromAsset.set_chain(static_cast(Chain::AVAX)); Proto::Asset toAsset; toAsset.set_chain(static_cast(Chain::THOR)); toAsset.set_symbol("RUNE"); auto&& [out, errorCode, error] = SwapBuilder::builder() .from(fromAsset) .to(toAsset) - .fromAddress("0xd0972E2312518Ca15A2304D56ff9cc0b7ea0Ea37") - .toAddress("thor1du84c7fj5y7kphq7zfyp8ugwxgrmy6n07xm9yj") - .vault("0x97673DF37E718dF203A834Bd095F69F6b4F314FA") - .router("0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146") - .fromAmount("5000000") - .toAmountLimit("418410520") + .fromAddress("0xbe6523017422A983B900b614Baeac51Ef7C1d0A3") + .toAddress("thor1ad6hapypumu7su5ad9qry2d74yt9d56fssa774") + .vault("0xa56f6Cb1D66cd80150b1ea79643b4C5900D6E36E") + .router("0x8f66c4ae756bebc49ec8b81966dd8bba9f127549") + .fromAmount("1000000") + .toAmountLimit("51638857") + .expirationPolicy(1775669796) + .affFeeAddress("t") + .affFeeRate("0") .build(); ASSERT_EQ(errorCode, 0); ASSERT_EQ(error, ""); - EXPECT_EQ(hex(out), "0a010012010018012201002a0100422a307844333742624535373434443733306131643938643844433937633432463043613436614437313436528d02328a020a01001284021fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a34313834313035323000000000000000000000000000000000000000000000000000000000000000"); + EXPECT_EQ(hex(out), "0a010012010018012201002a0100422a30783866363663346165373536626562633439656338623831393636646438626261396631323735343952ad0232aa020a010012a40244bc937b000000000000000000000000a56f6cb1d66cd80150b1ea79643b4c5900d6e36e000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000069d6922400000000000000000000000000000000000000000000000000000000000000443d3a54484f522e52554e453a74686f72316164366861707970756d753773753561643971727932643734797439643536667373613737343a35313633383835373a743a3000000000000000000000000000000000000000000000000000000000"); auto tx = Ethereum::Proto::SigningInput(); ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); // check fields - EXPECT_EQ(tx.to_address(), "0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146"); + EXPECT_EQ(tx.to_address(), "0x8f66c4ae756bebc49ec8b81966dd8bba9f127549"); ASSERT_TRUE(tx.transaction().has_contract_generic()); - Data vaultAddressBin = SwapTest_ethAddressStringToData("0x97673DF37E718dF203A834Bd095F69F6b4F314FA"); - EXPECT_EQ(hex(vaultAddressBin), "97673df37e718df203a834bd095f69f6b4f314fa"); - auto func = Ethereum::ABI::Function("deposit", std::vector>{ + Data vaultAddressBin = SwapTest_ethAddressStringToData("0xa56f6Cb1D66cd80150b1ea79643b4C5900D6E36E"); + EXPECT_EQ(hex(vaultAddressBin), "a56f6cb1d66cd80150b1ea79643b4c5900d6e36e"); + auto func = Ethereum::ABI::Function("depositWithExpiry", std::vector>{ std::make_shared(vaultAddressBin), - std::make_shared(parse_hex("0xdAC17F958D2ee523a2206206994597C13D831ec7")), - std::make_shared(uint256_t(5000000)), - std::make_shared("=:THOR.RUNE:thor1du84c7fj5y7kphq7zfyp8ugwxgrmy6n07xm9yj:418410520")}); + std::make_shared(parse_hex("0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E")), + std::make_shared(uint256_t(1000000)), + std::make_shared("=:THOR.RUNE:thor1ad6hapypumu7su5ad9qry2d74yt9d56fssa774:51638857:t:0"), + std::make_shared(uint256_t(1775669796))}); Data payload; func.encode(payload); - EXPECT_EQ(hex(payload), "1fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a34313834313035323000000000000000000000000000000000000000000000000000000000000000"); + EXPECT_EQ(hex(payload), "44bc937b000000000000000000000000a56f6cb1d66cd80150b1ea79643b4c5900d6e36e000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000069d6922400000000000000000000000000000000000000000000000000000000000000443d3a54484f522e52554e453a74686f72316164366861707970756d753773753561643971727932643734797439643536667373613737343a35313633383835373a743a3000000000000000000000000000000000000000000000000000000000"); EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "00"); EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); EXPECT_EQ(hex(TW::data(tx.private_key())), ""); // set few fields before signing - auto chainId = store(uint256_t(1)); + auto chainId = store(uint256_t(43114)); tx.set_chain_id(chainId.data(), chainId.size()); - auto nonce = store(uint256_t(7)); + auto nonce = store(uint256_t(6)); tx.set_nonce(nonce.data(), nonce.size()); - auto gasPrice = store(uint256_t(30000000000)); - tx.set_gas_price(gasPrice.data(), gasPrice.size()); - auto gasLimit = store(uint256_t(80000)); + auto maxInclusionFeePerGas = store(uint256_t(2000000000)); + auto maxFeePerGas = store(uint256_t(25000000000)); + tx.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + tx.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + auto gasLimit = store(uint256_t(108810)); tx.set_gas_limit(gasLimit.data(), gasLimit.size()); - auto privKey = parse_hex("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d"); + auto privKey = parse_hex("6649ba1d931059e7b419f97ee41c3f98b8f8054dfeb4cb57b9898bc5b9bbe318"); tx.set_private_key(privKey.data(), privKey.size()); // sign and encode resulting input Ethereum::Proto::SigningOutput output; - ANY_SIGN(tx, TWCoinTypeEthereum); - EXPECT_EQ(hex(output.encoded()), "02f90169010780808301388094d37bbe5744d730a1d98d8dc97c42f0ca46ad714680b901041fece7b400000000000000000000000097673df37e718df203a834bd095f69f6b4f314fa000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413d3a54484f522e52554e453a74686f7231647538346337666a3579376b706871377a667970387567777867726d79366e3037786d39796a3a34313834313035323000000000000000000000000000000000000000000000000000000000000000c001a01ff085d06b39d6efeb6663b065758f463564a555e41070ca8a8398bb1fc3426ba018bd8c6897f86d6ca4af7fe4cfac92e3fcb6224f896df62376ac1df556744ac6"); - // https://viewblock.io/thorchain/tx/56D2A63608E6EC09FA1D2934457CC09196683013905F69EDFC72B33EC68681AA - // https://etherscan.io/tx/0x56d2a63608e6ec09fa1d2934457cc09196683013905f69edfc72b33ec68681aa - // https://viewblock.io/thorchain/tx/BC1464CF3B56B07E40CF57985511814AEC9EAE2F1329CEE059A21529FDDFDB8C + ANY_SIGN(tx, TWCoinTypeAvalancheCChain); + EXPECT_EQ(hex(output.encoded()), "02f9019482a86a0684773594008505d21dba008301a90a948f66c4ae756bebc49ec8b81966dd8bba9f12754980b9012444bc937b000000000000000000000000a56f6cb1d66cd80150b1ea79643b4c5900d6e36e000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000069d6922400000000000000000000000000000000000000000000000000000000000000443d3a54484f522e52554e453a74686f72316164366861707970756d753773753561643971727932643734797439643536667373613737343a35313633383835373a743a3000000000000000000000000000000000000000000000000000000000c080a04a3a01941906579f1c6888771fe0621d66ee78998bfbb87219c0b5970235fc5ca03aefe4bb0c074f90798e078270c380930f4ae75366217f85535dd9be196a4244"); + // https://viewblock.io/thorchain/tx/B5E88D61157E7073995CA8729B75DAB2C1684A7B145DB711327CA4B8FF7DBDE7 + // https://snowtrace.io/tx/0xb5e88d61157e7073995ca8729b75dab2c1684a7b145db711327ca4b8ff7dbde7 + // https://thorchain.net/tx/B5E88D61157E7073995CA8729B75DAB2C1684A7B145DB711327CA4B8FF7DBDE7 } TEST(THORChainSwap, SwapAvaxBnb) { From 04534f52285ddc1a1576251ccd6cb283d8d2d55e Mon Sep 17 00:00:00 2001 From: Maxim Pestryakov Date: Fri, 10 Feb 2023 00:23:10 +0800 Subject: [PATCH 097/426] Add Kotlin Multiplatform support (#2894) --- .github/CODEOWNERS | 1 + .github/workflows/android-ci.yml | 18 +- .github/workflows/docker.yml | 2 +- .github/workflows/ios-ci.yml | 6 +- .github/workflows/kotlin-ci.yml | 48 + .github/workflows/linux-ci-sonarcloud.yml | 4 +- .github/workflows/linux-ci.yml | 4 +- .github/workflows/linux-sampleapp-ci.yml | 4 +- .github/workflows/wasm-ci.yml | 4 +- CMakeLists.txt | 8 +- codegen/bin/codegen | 12 + codegen/lib/code_generator.rb | 26 + codegen/lib/kotlin_helper.rb | 205 ++ codegen/lib/kotlin_jni_helper.rb | 113 + .../lib/templates/kotlin/android_class.erb | 56 + codegen/lib/templates/kotlin/android_enum.erb | 36 + .../lib/templates/kotlin/android_struct.erb | 19 + codegen/lib/templates/kotlin/common_class.erb | 55 + codegen/lib/templates/kotlin/common_enum.erb | 24 + .../lib/templates/kotlin/common_struct.erb | 13 + codegen/lib/templates/kotlin/ios_class.erb | 48 + codegen/lib/templates/kotlin/ios_enum.erb | 35 + codegen/lib/templates/kotlin/ios_struct.erb | 16 + codegen/lib/templates/kotlin/js_class.erb | 45 + codegen/lib/templates/kotlin/js_enum.erb | 32 + codegen/lib/templates/kotlin/js_struct.erb | 16 + codegen/lib/templates/kotlin/package.erb | 1 + codegen/lib/templates/kotlin_android.erb | 7 + codegen/lib/templates/kotlin_common.erb | 7 + codegen/lib/templates/kotlin_ios.erb | 7 + .../lib/templates/kotlin_jni/class_access.erb | 6 + .../lib/templates/kotlin_jni/compare_to.erb | 22 + .../lib/templates/kotlin_jni/enum_access.erb | 6 + .../templates/kotlin_jni/instance_access.erb | 14 + .../templates/kotlin_jni/instance_release.erb | 6 + codegen/lib/templates/kotlin_jni/method.erb | 8 + .../lib/templates/kotlin_jni/method_call.erb | 6 + .../templates/kotlin_jni/method_forward.erb | 118 + .../templates/kotlin_jni/method_prototype.erb | 9 + .../templates/kotlin_jni/parameter_access.erb | 23 + .../kotlin_jni/parameter_release.erb | 26 + .../lib/templates/kotlin_jni/proto_access.erb | 7 + .../templates/kotlin_jni/struct_access.erb | 8 + codegen/lib/templates/kotlin_jni_c.erb | 90 + codegen/lib/templates/kotlin_jni_h.erb | 66 + codegen/lib/templates/kotlin_js.erb | 7 + jni/{cpp => android}/AnySigner.c | 0 jni/{cpp => android}/AnySigner.h | 0 jni/cpp/Random.cpp | 4 +- jni/cpp/TWJNI.h | 2 +- jni/cpp/TWJNIData.cpp | 4 +- jni/cpp/TWJNIData.h | 2 +- jni/cpp/TWJNIString.cpp | 6 +- jni/cpp/TWJNIString.h | 2 +- jni/kotlin/AnySigner.c | 56 + jni/kotlin/AnySigner.h | 29 + kotlin/.editorconfig | 19 + kotlin/.gitignore | 2 + kotlin/README.md | 5 + kotlin/build-logic/build.gradle.kts | 14 + kotlin/build-logic/settings.gradle.kts | 23 + .../convention.file-generation.gradle.kts | 86 + .../convention.maven-publish.gradle.kts | 21 + kotlin/build.gradle.kts | 20 + kotlin/gradle.properties | 12 + kotlin/gradle/libs.versions.toml | 14 + kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61574 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + kotlin/gradlew | 244 ++ kotlin/gradlew.bat | 92 + kotlin/kotlin-js-store/yarn.lock | 2020 +++++++++++++++++ kotlin/settings.gradle.kts | 27 + kotlin/wallet-core-kotlin/.gitignore | 3 + kotlin/wallet-core-kotlin/build.gradle.kts | 130 ++ .../kotlin/com/trustwallet/core/AnySigner.kt | 15 + .../kotlin/com/trustwallet/core/AnySigner.kt | 32 + .../kotlin/com/trustwallet/core/AnySigner.kt | 19 + .../com/trustwallet/core/ByteArrayExt.kt | 18 + .../kotlin/com/trustwallet/core/StringExt.kt | 17 + .../src/jsMain/kotlin/WalletCore.kt | 27 + .../kotlin/com/trustwallet/core/AnySigner.kt | 25 + .../kotlin/com/trustwallet/core/UInt8Array.kt | 17 + tools/install-kotlin-dependencies | 7 + tools/kotlin-build | 8 + tools/kotlin-release | 17 + 85 files changed, 4309 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/kotlin-ci.yml create mode 100644 codegen/lib/kotlin_helper.rb create mode 100644 codegen/lib/kotlin_jni_helper.rb create mode 100644 codegen/lib/templates/kotlin/android_class.erb create mode 100644 codegen/lib/templates/kotlin/android_enum.erb create mode 100644 codegen/lib/templates/kotlin/android_struct.erb create mode 100644 codegen/lib/templates/kotlin/common_class.erb create mode 100644 codegen/lib/templates/kotlin/common_enum.erb create mode 100644 codegen/lib/templates/kotlin/common_struct.erb create mode 100644 codegen/lib/templates/kotlin/ios_class.erb create mode 100644 codegen/lib/templates/kotlin/ios_enum.erb create mode 100644 codegen/lib/templates/kotlin/ios_struct.erb create mode 100644 codegen/lib/templates/kotlin/js_class.erb create mode 100644 codegen/lib/templates/kotlin/js_enum.erb create mode 100644 codegen/lib/templates/kotlin/js_struct.erb create mode 100644 codegen/lib/templates/kotlin/package.erb create mode 100644 codegen/lib/templates/kotlin_android.erb create mode 100644 codegen/lib/templates/kotlin_common.erb create mode 100644 codegen/lib/templates/kotlin_ios.erb create mode 100644 codegen/lib/templates/kotlin_jni/class_access.erb create mode 100644 codegen/lib/templates/kotlin_jni/compare_to.erb create mode 100644 codegen/lib/templates/kotlin_jni/enum_access.erb create mode 100644 codegen/lib/templates/kotlin_jni/instance_access.erb create mode 100644 codegen/lib/templates/kotlin_jni/instance_release.erb create mode 100644 codegen/lib/templates/kotlin_jni/method.erb create mode 100644 codegen/lib/templates/kotlin_jni/method_call.erb create mode 100644 codegen/lib/templates/kotlin_jni/method_forward.erb create mode 100644 codegen/lib/templates/kotlin_jni/method_prototype.erb create mode 100644 codegen/lib/templates/kotlin_jni/parameter_access.erb create mode 100644 codegen/lib/templates/kotlin_jni/parameter_release.erb create mode 100644 codegen/lib/templates/kotlin_jni/proto_access.erb create mode 100644 codegen/lib/templates/kotlin_jni/struct_access.erb create mode 100644 codegen/lib/templates/kotlin_jni_c.erb create mode 100644 codegen/lib/templates/kotlin_jni_h.erb create mode 100644 codegen/lib/templates/kotlin_js.erb rename jni/{cpp => android}/AnySigner.c (100%) rename jni/{cpp => android}/AnySigner.h (100%) create mode 100644 jni/kotlin/AnySigner.c create mode 100644 jni/kotlin/AnySigner.h create mode 100644 kotlin/.editorconfig create mode 100644 kotlin/.gitignore create mode 100644 kotlin/README.md create mode 100644 kotlin/build-logic/build.gradle.kts create mode 100644 kotlin/build-logic/settings.gradle.kts create mode 100644 kotlin/build-logic/src/main/kotlin/convention.file-generation.gradle.kts create mode 100644 kotlin/build-logic/src/main/kotlin/convention.maven-publish.gradle.kts create mode 100644 kotlin/build.gradle.kts create mode 100644 kotlin/gradle.properties create mode 100644 kotlin/gradle/libs.versions.toml create mode 100644 kotlin/gradle/wrapper/gradle-wrapper.jar create mode 100644 kotlin/gradle/wrapper/gradle-wrapper.properties create mode 100755 kotlin/gradlew create mode 100755 kotlin/gradlew.bat create mode 100644 kotlin/kotlin-js-store/yarn.lock create mode 100644 kotlin/settings.gradle.kts create mode 100644 kotlin/wallet-core-kotlin/.gitignore create mode 100644 kotlin/wallet-core-kotlin/build.gradle.kts create mode 100644 kotlin/wallet-core-kotlin/src/androidMain/kotlin/com/trustwallet/core/AnySigner.kt create mode 100644 kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt create mode 100644 kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt create mode 100644 kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt create mode 100644 kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/StringExt.kt create mode 100644 kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt create mode 100644 kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt create mode 100644 kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/UInt8Array.kt create mode 100755 tools/install-kotlin-dependencies create mode 100755 tools/kotlin-build create mode 100755 tools/kotlin-release diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 79869e12cc8..533f10e3dcd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,3 +4,4 @@ # review when someone opens a pull request. * @hewigovens @rsrbk @milerius +kotlin/ @MaximPestryakov diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 5203adc04dc..fc7f61fe874 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -8,15 +8,19 @@ on: jobs: build: - - runs-on: macos-12 + runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: - java-version: 11 + java-version: '11' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Install system dependencies run: | @@ -28,7 +32,7 @@ jobs: - name: Cache internal dependencies id: internal_cache - uses: actions/cache@v1.1.2 + uses: actions/cache@v3 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} @@ -36,7 +40,7 @@ jobs: - name: Install internal dependencies run: tools/install-dependencies if: steps.internal_cache.outputs.cache-hit != 'true' - + - name: Generate files run: tools/generate-files diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 369efae9cbd..fb7bf2d691a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -16,7 +16,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Lint Dockerfile run: | curl -L https://github.com/hadolint/hadolint/releases/download/v1.17.6/hadolint-Linux-x86_64 -o hadolint && chmod +x hadolint diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index 61813885a4d..4490e8a48dd 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -8,16 +8,16 @@ on: jobs: build: - runs-on: macos-12 + runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install system dependencies run: | tools/install-sys-dependencies-mac tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache - uses: actions/cache@v1.1.2 + uses: actions/cache@v3 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} diff --git a/.github/workflows/kotlin-ci.yml b/.github/workflows/kotlin-ci.yml new file mode 100644 index 00000000000..3e88aa6aa45 --- /dev/null +++ b/.github/workflows/kotlin-ci.yml @@ -0,0 +1,48 @@ +name: Kotlin CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Install system dependencies + run: | + tools/install-sys-dependencies-mac + tools/install-rust-dependencies + + - name: Install Kotlin Dependencies + run: tools/install-kotlin-dependencies + + - name: Cache internal dependencies + id: internal_cache + uses: actions/cache@v3 + with: + path: build/local + key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + + - name: Install internal dependencies + run: tools/install-dependencies + if: steps.internal_cache.outputs.cache-hit != 'true' + + - name: Generate files + run: tools/generate-files + + - name: Build Kotlin Multiplatform + run: tools/kotlin-build diff --git a/.github/workflows/linux-ci-sonarcloud.yml b/.github/workflows/linux-ci-sonarcloud.yml index dd1829a065c..5acae199798 100644 --- a/.github/workflows/linux-ci-sonarcloud.yml +++ b/.github/workflows/linux-ci-sonarcloud.yml @@ -11,14 +11,14 @@ jobs: if: github.event.pull_request.head.repo.fork == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install system dependencies run: | tools/install-sys-dependencies-linux tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache - uses: actions/cache@v1.1.2 + uses: actions/cache@v3 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 944ee976432..84d53924e49 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -10,14 +10,14 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install system dependencies run: | tools/install-sys-dependencies-linux tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache - uses: actions/cache@v1.1.2 + uses: actions/cache@v3 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index c963437f5d3..acd7026a04c 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -10,14 +10,14 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install system dependencies run: | tools/install-sys-dependencies-linux tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache - uses: actions/cache@v1.1.2 + uses: actions/cache@v3 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} diff --git a/.github/workflows/wasm-ci.yml b/.github/workflows/wasm-ci.yml index 3eba0a86a70..013b6be472f 100644 --- a/.github/workflows/wasm-ci.yml +++ b/.github/workflows/wasm-ci.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install system dependencies run: | @@ -21,7 +21,7 @@ jobs: - name: Cache internal dependencies id: internal_cache - uses: actions/cache@v1.1.2 + uses: actions/cache@v3 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} diff --git a/CMakeLists.txt b/CMakeLists.txt index a6dbdf3c1f0..f097a622916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,13 @@ include(cmake/Protobuf.cmake) # Source files if (${ANDROID}) message("Configuring for JNI") - file(GLOB_RECURSE sources src/*.c src/*.cc src/*.cpp src/*.h jni/cpp/*.c jni/cpp/*.cpp jni/cpp/*.h jni/cpp/*.c) + file(GLOB_RECURSE core_sources src/*.c src/*.cc src/*.cpp src/*.h jni/cpp/*.c jni/cpp/*.cpp jni/cpp/*.h) + if (${KOTLIN}) + file(GLOB_RECURSE specific_sources jni/kotlin/*.h jni/kotlin/*.c) + else () + file(GLOB_RECURSE specific_sources jni/android/*.h jni/android/*.c) + endif() + set(sources ${core_sources} ${specific_sources}) add_library(TrustWalletCore SHARED ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) find_library(log-lib log) if (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a") diff --git a/codegen/bin/codegen b/codegen/bin/codegen index 8fc86c7e86e..10dfda8d9ff 100755 --- a/codegen/bin/codegen +++ b/codegen/bin/codegen @@ -22,6 +22,7 @@ options.jni_h = true options.jni_c = true options.wasm_cpp = true options.ts_declaration = true +options.kotlin = true OptionParser.new do |opts| opts.banner = 'Usage: codegen [options]' @@ -50,6 +51,9 @@ OptionParser.new do |opts| opts.on('-t', '--typescript-declaration', "Generate typescript declaration file Default: #{options.ts_declaration}") do |v| options.ts_declaration = v end + opts.on('-k', '--kotlin', "Generate Kotlin code. Default: #{options.kotlin}") do |v| + options.kotlin = v + end opts.on_tail('-h', '--help', 'Show this message') do puts opts exit @@ -88,3 +92,11 @@ end if options.ts_declaration generator.render_ts_declaration end +if options.kotlin + generator.render_kotlin_common + generator.render_kotlin_android + generator.render_kotlin_ios + generator.render_kotlin_js + generator.render_kotlin_jni_h + generator.render_kotlin_jni_c +end diff --git a/codegen/lib/code_generator.rb b/codegen/lib/code_generator.rb index 7896c2f8dca..d870418b222 100644 --- a/codegen/lib/code_generator.rb +++ b/codegen/lib/code_generator.rb @@ -7,6 +7,8 @@ require 'swift_helper' require 'wasm_cpp_helper' require 'ts_helper' +require 'kotlin_helper' +require 'kotlin_jni_helper' # Code generation class CodeGenerator @@ -103,6 +105,30 @@ def render_ts_declaration TsHelper.combine_declaration_files() end + def render_kotlin_common + render_template(header: nil, template: 'kotlin_common.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/commonMain/generated/com/trustwallet/core', extension: 'kt') + end + + def render_kotlin_android + render_template(header: nil, template: 'kotlin_android.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/androidMain/generated/com/trustwallet/core', extension: 'kt') + end + + def render_kotlin_ios + render_template(header: nil, template: 'kotlin_ios.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/iosMain/generated/com/trustwallet/core', extension: 'kt') + end + + def render_kotlin_js + render_template(header: nil, template: 'kotlin_js.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/jsMain/generated/com/trustwallet/core', extension: 'kt') + end + + def render_kotlin_jni_h + render_template(header: 'copyright_header.erb', template: 'kotlin_jni_h.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/androidMain/cpp/generated', extension: 'h') + end + + def render_kotlin_jni_c + render_template(header: 'copyright_header.erb', template: 'kotlin_jni_c.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/androidMain/cpp/generated', extension: 'c') + end + def render(file, locals = {}) @locals = locals path = File.expand_path(file, File.join(File.dirname(__FILE__), 'templates')) diff --git a/codegen/lib/kotlin_helper.rb b/codegen/lib/kotlin_helper.rb new file mode 100644 index 00000000000..bd40ac74d97 --- /dev/null +++ b/codegen/lib/kotlin_helper.rb @@ -0,0 +1,205 @@ +# frozen_string_literal: true + +module KotlinHelper + # Transforms an interface name to a Java method name + def self.format_name(name) + return 'equals' if name == 'Equal' + + result = name + match = /^([A-Z]+)/.match(name) + result = name.sub(match[1], match[1].downcase) unless match.nil? + + result.sub(/_/, '') + end + + def self.parameters(params) + names = params.map do |param| + name = fix_name(param.name) + "#{name}: #{type(param.type)}" + end + names.join(', ') + end + + def self.calling_parameters_ios(params) + names = params.map do |param| + name = fix_name(param.name) + "#{name}#{convert_calling_type_ios(param.type)}" + end + names.join(', ') + end + + def self.calling_parameters_android(params) + names = params.map do |param| + fix_name(param.name) + end + names.join(', ') + end + + def self.calling_parameters_js(params) + names = params.map do |param| + name = fix_name(param.name) + "#{name}#{convert_calling_type_js(param.type)}" + end + names.join(', ') + end + + def self.fix_name(name) + case name + when '' + "value" + when 'val' + "value" + when 'return' + '`return`' + else + name + end + end + + def self.convert_calling_type_ios(t) + case t.name + when :data + "#{if t.is_nullable then '?' else '' end}.toTwData()" + when :string + "#{if t.is_nullable then '?' else '' end}.toTwString()" + else + if t.is_enum + "#{if t.is_nullable then '?' else '' end}.value" + elsif t.is_class + "#{if t.is_nullable then '?' else '' end}.pointer" + else + '' + end + end + end + + def self.convert_calling_type_js(t) + case t.name + when :data + "#{if t.is_nullable then '?' else '' end}.toUInt8Array()" + else + if t.is_enum + "#{if t.is_nullable then '?' else '' end}._value" + elsif t.is_class + "#{if t.is_nullable then '?' else '' end}._value" + else + '' + end + end + end + + def self.convert_calling_return_type_ios(t, expression = '') + case t.name + when :data + "#{expression}.readTwBytes()#{if t.is_nullable then '' else '!!' end}" + when :string + "#{expression}.fromTwString()#{if t.is_nullable then '' else '!!' end}" + else + if t.is_enum + "#{t.name}.fromValue(#{expression})#{if t.is_nullable then '' else '!!' end}" + elsif t.is_class + if t.is_nullable + "#{expression}?.let { #{t.name}(it) }" + else + "#{t.name}(#{expression}!!)" + end + else + expression + end + end + end + + def self.convert_calling_return_type_js(t, expression = '') + nullable = "#{if t.is_nullable then '?' else '' end}" + case t.name + when :void + expression + when :data + "#{expression}.unsafeCast()#{nullable}.toByteArray()" + when :int + "#{expression}.unsafeCast().toInt()" + when :uint8 + "#{expression}.unsafeCast().toByte().toUByte()" + when :uint16 + "#{expression}.unsafeCast().toShort().toUShort()" + when :uint32 + "#{expression}.unsafeCast().toInt().toUInt()" + when :uint64 + "#{expression}.unsafeCast().toLong().toULong()" + when :int8 + "#{expression}.unsafeCast().toByte()" + when :int16 + "#{expression}.unsafeCast().toShort()" + when :int32 + "#{expression}.unsafeCast().toInt()" + when :int64 + "#{expression}.unsafeCast().toLong()" + when :size + "#{expression}.unsafeCast().toLong().toULong()" + else + if t.is_enum + "#{t.name}.fromValue(#{expression})#{if t.is_nullable then '' else '!!' end}" + elsif t.is_class + if t.is_nullable + "#{expression}.unsafeCast()?.let { #{t.name}(it, Unit) }" + else + "#{t.name}(#{expression}, Unit)" + end + else + "#{expression} as #{type(t)}" + end + end + end + + def self.arguments(params) + params.map do |param| + param.name || 'value' + end.join(', ') + end + + def self.type(t) + nullable = "#{if t.is_nullable then '?' else '' end}" + case t.name + when :void + "" + when :bool + "Boolean#{nullable}" + when :int + "Int#{nullable}" + when :uint8 + "UByte#{nullable}" + when :uint16 + "UShort#{nullable}" + when :uint32 + "UInt#{nullable}" + when :uint64 + "ULong#{nullable}" + when :int8 + "Byte#{nullable}" + when :int16 + "Short#{nullable}" + when :int32 + "Int#{nullable}" + when :int64 + "Long#{nullable}" + when :size + "ULong#{nullable}" + when :data + "ByteArray#{nullable}" + when :string + "String#{nullable}" + else + "#{t.name}#{nullable}" + end + end + + def self.return_type(t) + case t.name + when :void + "" + else + ": #{type(t)}" + end + end + +end diff --git a/codegen/lib/kotlin_jni_helper.rb b/codegen/lib/kotlin_jni_helper.rb new file mode 100644 index 00000000000..b1303a6d69e --- /dev/null +++ b/codegen/lib/kotlin_jni_helper.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +module KotlinJniHelper + # Transforms an interface name to a JNI method name + def self.format_name(name) + return 'equals' if name == 'Equal' + + result = name + match = /^([A-Z]+)/.match(name) + result = name.sub(match[1], match[1].downcase) unless match.nil? + + result.sub(/_/, '') + end + + # Transforms a method/property name to a JNI function name + def self.function_name(entity:, function:, native_prefix: false) + "Java_com_trustwallet_core_#{entity.name}_#{format_name(function.name)}" + end + + # Transforms a proto name name to a JNI class name + def self.proto_to_class(name) + parts = name.split('_') + return nil if parts.count < 3 || parts[0] != 'TW' + + if parts.count == 3 + "wallet/core/jni/proto/Common$#{parts.last}" + else + "wallet/core/jni/proto/#{parts[1]}$#{parts[3]}" + end + end + + def self.parameters(params) + names = params.map do |param| + ", #{type(param.type)} #{param.name || 'value'}" + end + names.join('') + end + + def self.arguments(params) + params.map do |param| + if param.type.is_class + (param.name || 'value') + 'Instance' + elsif param.type.is_struct + '*' + (param.name || 'value') + 'Instance' + elsif param.type.name == :data + (param.name || 'value') + 'Data' + elsif param.type.name == :string + (param.name || 'value') + 'String' + elsif param.type.is_enum + (param.name || 'value') + 'Value' + elsif param.type.is_proto + (param.name || 'value') + 'Data' + else + param.name || 'value' + end + end + end + + def self.type(t) + case t.name + when :void + 'void' + when :bool + 'jboolean' + when :int + 'jint' + when :uint8 + 'jchar' + when :uint16 + 'jshort' + when :uint32 + 'jint' + when :uint64 + 'jlong' + when :int8 + 'jbyte' + when :int16 + 'jshort' + when :int32 + 'jint' + when :int64 + 'jlong' + when :size + 'jsize' + when :data + 'jbyteArray' + when 'Data' + 'jbyteArray' + when :string + 'jstring' + else + if t.is_class || t.is_struct + 'jobject' + elsif t.is_enum + 'jobject' + elsif t.is_proto + 'jobject' + else + raise "Invalid type #{t.name}" + end + end + end + + def self.compareMethod(entity) + FunctionDecl.new( + name: 'compareTo', + entity: entity, + is_method: true, + return_type: TypeDecl.new(name: :int), + parameters: [Parameter.new(name: 'thisObject', type: entity.type), Parameter.new(name: 'other', type: entity.type)], + static: false) + end +end diff --git a/codegen/lib/templates/kotlin/android_class.erb b/codegen/lib/templates/kotlin/android_class.erb new file mode 100644 index 00000000000..f3006d57526 --- /dev/null +++ b/codegen/lib/templates/kotlin/android_class.erb @@ -0,0 +1,56 @@ +<%= render('kotlin/package.erb') %> + +<% constructors = entity.static_methods.select { |method| method.name.start_with?('Create') } -%> +<% methods = entity.methods.select { |method| not method.name.start_with?('Delete') } -%> +<% static_methods = entity.static_methods.select { |method| not method.name.start_with?('Create') } -%> +actual class <%= entity.name %> private constructor( + private val nativeHandle: Long, +) { +<%# Constructors -%> +<%- constructors.each do |constructor| -%> + + actual constructor(<%= KotlinHelper.parameters(constructor.parameters) %>) : this(<%= KotlinHelper.format_name(constructor.name) %>(<%= KotlinHelper.calling_parameters_android(constructor.parameters) %>)) +<% end -%> +<%# Property declarations -%> +<% entity.properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + @JvmName("<%= KotlinHelper.format_name(property.name) %>") + external get +<% end -%> +<%# Method declarations -%> +<% methods.each do |method| -%> + + @JvmName("<%= KotlinHelper.format_name(method.name) %>") + actual external fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> +<% end -%> +<% if entity.static_properties.any? || static_methods.any? || constructors.any? -%> + + <%= if entity.static_properties.any? || static_methods.any? then "actual" else "private" end %> companion object { +<%# Static property declarations -%> +<% entity.static_properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + @JvmName("<%= KotlinHelper.format_name(property.name) %>") + external get +<% end -%> +<%# Static method declarations -%> +<% static_methods.each do |method| -%> + + @JvmStatic + @JvmName("<%= KotlinHelper.format_name(method.name) %>") + actual external fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> +<% end -%> + + @JvmStatic + @JvmName("createFromNative") + private fun createFromNative(nativeHandle: Long) = <%= entity.name %>(nativeHandle) +<%- constructors.each do |constructor| -%> + + @JvmStatic + @JvmName("<%= KotlinHelper.format_name(constructor.name) %>") + private external fun <%= KotlinHelper.format_name(constructor.name) %>(<%= KotlinHelper.parameters(constructor.parameters) %>): Long +<%- end -%> + } +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/android_enum.erb b/codegen/lib/templates/kotlin/android_enum.erb new file mode 100644 index 00000000000..4c9a44e523b --- /dev/null +++ b/codegen/lib/templates/kotlin/android_enum.erb @@ -0,0 +1,36 @@ +<%= render('kotlin/package.erb') %> + +actual enum class <%= entity.name %>( + @get:JvmName("value") + internal val value: UInt, +) { +<%# Cases -%> +<% entity.cases.each_with_index do |c, i| -%> + <%= c.name %>(<%= c.value %>u), +<% end -%> + ; +<%# Property declarations -%> +<%- entity.properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + @JvmName("<%= KotlinHelper.format_name(property.name) %>") + external get +<%- end -%> +<%# Method declarations -%> +<%- entity.methods.each do |method| -%> +<%- next if method.name.start_with?('Delete') -%> + + @JvmName("<%= KotlinHelper.format_name(method.name) %>") + actual external fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> +<%- end -%> +<%# Value -%> +<% if entity.cases.any? { |e| !e.value.nil? } -%> + + internal companion object { + @JvmStatic + @JvmName("createFromValue") + internal fun fromValue(value: UInt): <%= entity.name %>? = + values().firstOrNull { it.value == value } + } +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/android_struct.erb b/codegen/lib/templates/kotlin/android_struct.erb new file mode 100644 index 00000000000..099704dd35b --- /dev/null +++ b/codegen/lib/templates/kotlin/android_struct.erb @@ -0,0 +1,19 @@ +<%= render('kotlin/package.erb') %> + +actual object <%= entity.name %> { +<%# Static property declarations -%> +<% entity.static_properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + @JvmName("<%= KotlinHelper.format_name(property.name) %>") + external get +<% end -%> +<%# Static method declarations -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') -%> + + @JvmStatic + @JvmName("<%= KotlinHelper.format_name(method.name) %>") + actual external fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/common_class.erb b/codegen/lib/templates/kotlin/common_class.erb new file mode 100644 index 00000000000..b47c1eb85f0 --- /dev/null +++ b/codegen/lib/templates/kotlin/common_class.erb @@ -0,0 +1,55 @@ +<%= render('kotlin/package.erb') %> + +<% constructors = entity.static_methods.select { |method| method.name.start_with?('Create') } -%> +<% methods = entity.methods.select { |method| not method.name.start_with?('Delete') } -%> +<% static_methods = entity.static_methods.select { |method| not method.name.start_with?('Create') } -%> +<% if constructors.one? -%> +expect class <%= entity.name %>( +<%- constructors.first.parameters.each do |parameter| -%> + <%= KotlinHelper.parameters([parameter]) %>, +<%- end -%> +) { +<%- else -%> +expect class <%= entity.name %> { +<%# Constructors -%> +<% if constructors.any? -%> + +<% end -%> +<%- constructors.each do |constructor| -%> + constructor(<%= KotlinHelper.parameters(constructor.parameters) %>) +<%- end -%> +<% end -%> +<%# Property declarations -%> +<% if entity.properties.any? -%> + +<% end -%> +<% entity.properties.each do |property| -%> + val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> +<% end -%> +<%# Method declarations -%> +<% if methods.any? -%> + +<% end -%> +<% methods.each do |method| -%> + fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> +<% end -%> +<% if entity.static_properties.any? || static_methods.any? -%> + + companion object { +<%# Static property declarations -%> +<% if entity.static_properties.any? -%> + +<% end -%> +<% entity.static_properties.each do |property| -%> + val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> +<% end -%> +<%# Static method declarations -%> +<% if static_methods.any? -%> + +<% end -%> +<% static_methods.each do |method| -%> + fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> +<% end -%> + } +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/common_enum.erb b/codegen/lib/templates/kotlin/common_enum.erb new file mode 100644 index 00000000000..87f0d814598 --- /dev/null +++ b/codegen/lib/templates/kotlin/common_enum.erb @@ -0,0 +1,24 @@ +<%= render('kotlin/package.erb') %> + +expect enum class <%= entity.name %> { +<%# Cases -%> +<% entity.cases.each_with_index do |c, i| -%> + <%= c.name %>, +<% end -%> + ; +<%# Property declarations -%> +<%- entity.properties.each do |property| -%> + val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> +<%- end -%> +<%# Method declarations -%> +<% if entity.methods.any? -%> + +<% end -%> +<%- entity.methods.each do |method| -%> +<%- next if method.name.start_with?('Delete') -%> + fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> +<%- end -%> +<%# Value -%> +<% if entity.cases.any? { |e| !e.value.nil? } -%> +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/common_struct.erb b/codegen/lib/templates/kotlin/common_struct.erb new file mode 100644 index 00000000000..39e085799ea --- /dev/null +++ b/codegen/lib/templates/kotlin/common_struct.erb @@ -0,0 +1,13 @@ +<%= render('kotlin/package.erb') %> + +expect object <%= entity.name %> { +<%# Static property declarations -%> +<% entity.static_properties.each do |property| -%> + val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> +<% end -%> +<%# Static method declarations -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') -%> + fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/ios_class.erb b/codegen/lib/templates/kotlin/ios_class.erb new file mode 100644 index 00000000000..886be145b10 --- /dev/null +++ b/codegen/lib/templates/kotlin/ios_class.erb @@ -0,0 +1,48 @@ +<%= render('kotlin/package.erb') %> + +import cnames.structs.TW<%= entity.name %> +import kotlinx.cinterop.CPointer + +<% constructors = entity.static_methods.select { |method| method.name.start_with?('Create') } -%> +<% methods = entity.methods.select { |method| not method.name.start_with?('Delete') } -%> +<% static_methods = entity.static_methods.select { |method| not method.name.start_with?('Create') } -%> +actual class <%= entity.name %> internal constructor( + internal val pointer: CPointer>, +) { +<%# Constructors -%> +<%- constructors.each do |constructor| -%> + + actual constructor(<%= KotlinHelper.parameters(constructor.parameters) %>) : this( + TW<%= entity.name %><%= constructor.name %>(<%= KotlinHelper.calling_parameters_ios(constructor.parameters) %>)!! + ) +<% end -%> +<%# Property declarations -%> +<% entity.properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + get() = <%= KotlinHelper.convert_calling_return_type_ios(property.return_type, "TW#{entity.name}#{property.name}(pointer)") %> +<% end -%> +<%# Method declarations -%> +<% methods.each do |method| -%> + + actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> = + <%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(pointer#{', ' if not method.parameters.one?}#{KotlinHelper.calling_parameters_ios(method.parameters.drop(1))})") %> +<% end -%> +<% if entity.static_properties.any? || static_methods.any? -%> + + actual companion object { +<%# Static property declarations -%> +<% entity.static_properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + get() = TW<%= entity.name %><%= property.name %>()<%= KotlinHelper.convert_calling_return_type_ios(property.return_type) %> +<% end -%> +<%# Static method declarations -%> +<% static_methods.each do |method| -%> + + actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> = + <%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(#{KotlinHelper.calling_parameters_ios(method.parameters)})") %> +<% end -%> + } +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/ios_enum.erb b/codegen/lib/templates/kotlin/ios_enum.erb new file mode 100644 index 00000000000..cae9ff33f75 --- /dev/null +++ b/codegen/lib/templates/kotlin/ios_enum.erb @@ -0,0 +1,35 @@ +<%= render('kotlin/package.erb') %> + +import com.trustwallet.core.<%= "TW#{entity.name}" %>.* + +<% type = ": TW#{entity.name}" -%> +actual enum class <%= entity.name %>( + internal val value<%= type %>, +) { +<%# Cases -%> +<% entity.cases.each_with_index do |c, i| -%> + <%= c.name %>(TW<%= entity.name %><%= c.name %>), +<% end -%> + ; +<%# Property declarations -%> +<%- entity.properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + get() = <%= KotlinHelper.convert_calling_return_type_ios(property.return_type, "TW#{entity.name}#{property.name}(value)") %> +<%- end -%> +<%# Method declarations -%> +<%- entity.methods.each do |method| -%> +<%- next if method.name.start_with?('Delete') -%> + + actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> = + TW<%= entity.name %><%= method.name %>(value<%= ', ' if not method.parameters.one? %><%= KotlinHelper.calling_parameters_ios(method.parameters.drop(1)) %>)<%= KotlinHelper.convert_calling_return_type_ios(method.return_type) %> +<%- end -%> +<%# Value -%> +<% if entity.cases.any? { |e| !e.value.nil? } -%> + + internal companion object { + internal fun fromValue(value<%= type %>): <%= entity.name %>? = + values().firstOrNull { it.value == value } + } +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/ios_struct.erb b/codegen/lib/templates/kotlin/ios_struct.erb new file mode 100644 index 00000000000..6bf3da876ee --- /dev/null +++ b/codegen/lib/templates/kotlin/ios_struct.erb @@ -0,0 +1,16 @@ +<%= render('kotlin/package.erb') %> + +actual object <%= entity.name %> { +<%# Static property declarations -%> +<% entity.static_properties.each do |property| -%> + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + get() = TODO() +<% end -%> +<%# Static method declarations -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') -%> + + actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> = + <%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(#{KotlinHelper.calling_parameters_ios(method.parameters)})") %> +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/js_class.erb b/codegen/lib/templates/kotlin/js_class.erb new file mode 100644 index 00000000000..187cbbd940b --- /dev/null +++ b/codegen/lib/templates/kotlin/js_class.erb @@ -0,0 +1,45 @@ +<%= render('kotlin/package.erb') %> + +<% constructors = entity.static_methods.select { |method| method.name.start_with?('Create') } -%> +<% methods = entity.methods.select { |method| not method.name.start_with?('Delete') } -%> +<% static_methods = entity.static_methods.select { |method| not method.name.start_with?('Create') } -%> +actual class <%= entity.name %> constructor( + val _value: dynamic, + unit: Unit, // Hack +) { +<%# Constructors -%> +<%- constructors.each do |constructor| -%> + + actual constructor(<%= KotlinHelper.parameters(constructor.parameters) %>) : + this(WalletCore.Instance.<%= entity.name %>.<%= WasmCppHelper.function_name(entity: entity, function: constructor) %>(<%= KotlinHelper.calling_parameters_js(constructor.parameters) %>), Unit) +<% end -%> +<%# Property declarations -%> +<% entity.properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + get() = <%= KotlinHelper.convert_calling_return_type_js(property.return_type, "_value.#{WasmCppHelper.function_name(entity: entity, function: property)}()") %> +<% end -%> +<%# Method declarations -%> +<% methods.each do |method| -%> + + actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> = + <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "_value.#{WasmCppHelper.function_name(entity: entity, function: method)}(#{KotlinHelper.calling_parameters_js(method.parameters.drop(1))})") %> +<% end -%> +<% if entity.static_properties.any? || static_methods.any? -%> + + <%= if entity.static_properties.any? || static_methods.any? then "actual" else "private" end %> companion object { +<%# Static property declarations -%> +<% entity.static_properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + get() = TODO() +<% end -%> +<%# Static method declarations -%> +<% static_methods.each do |method| -%> + + actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> = + <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "WalletCore.Instance.#{entity.name}.#{WasmCppHelper.function_name(entity: entity, function: method)}(#{KotlinHelper.calling_parameters_js(method.parameters)})") %> +<% end -%> + } +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/js_enum.erb b/codegen/lib/templates/kotlin/js_enum.erb new file mode 100644 index 00000000000..d54ffcf6630 --- /dev/null +++ b/codegen/lib/templates/kotlin/js_enum.erb @@ -0,0 +1,32 @@ +<%= render('kotlin/package.erb') %> + +actual enum class <%= entity.name %>( + internal val _value: dynamic, +) { +<%# Cases -%> +<% entity.cases.each_with_index do |c, i| -%> + <%= c.name %>(WalletCore.Instance.<%= entity.name %>.<%= KotlinHelper.fix_name(WasmCppHelper.format_name(c.name)) %>), +<% end -%> + ; +<%# Property declarations -%> +<%- entity.properties.each do |property| -%> + + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + get() = <%= KotlinHelper.convert_calling_return_type_js(property.return_type, "_value.#{WasmCppHelper.function_name(entity: entity, function: property)}()") %> +<%- end -%> +<%# Method declarations -%> +<%- entity.methods.each do |method| -%> +<%- next if method.name.start_with?('Delete') -%> + + actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> = + <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "_value.#{WasmCppHelper.function_name(entity: entity, function: method)}(#{KotlinHelper.calling_parameters_js(method.parameters.drop(1))})") %> +<%- end -%> +<%# Value -%> +<% if entity.cases.any? { |e| !e.value.nil? } -%> + + internal companion object { + internal fun fromValue(value: dynamic): <%= entity.name %>? = + values().firstOrNull { it._value == value } + } +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/js_struct.erb b/codegen/lib/templates/kotlin/js_struct.erb new file mode 100644 index 00000000000..80fd05feb72 --- /dev/null +++ b/codegen/lib/templates/kotlin/js_struct.erb @@ -0,0 +1,16 @@ +<%= render('kotlin/package.erb') %> + +actual object <%= entity.name %> { +<%# Static property declarations -%> +<% entity.static_properties.each do |property| -%> + actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> + get() = TODO() +<% end -%> +<%# Static method declarations -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') -%> + + actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> = + <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "WalletCore.Instance.#{entity.name}.#{WasmCppHelper.function_name(entity: entity, function: method)}(#{KotlinHelper.calling_parameters_js(method.parameters)})") %> +<% end -%> +} diff --git a/codegen/lib/templates/kotlin/package.erb b/codegen/lib/templates/kotlin/package.erb new file mode 100644 index 00000000000..64142a21c30 --- /dev/null +++ b/codegen/lib/templates/kotlin/package.erb @@ -0,0 +1 @@ +package com.trustwallet.core \ No newline at end of file diff --git a/codegen/lib/templates/kotlin_android.erb b/codegen/lib/templates/kotlin_android.erb new file mode 100644 index 00000000000..a9767e71d97 --- /dev/null +++ b/codegen/lib/templates/kotlin_android.erb @@ -0,0 +1,7 @@ +<%- if entity.is_a?(EnumDecl) -%> +<%= render('kotlin/android_enum.erb') -%> +<%- elsif entity.is_struct -%> +<%= render('kotlin/android_struct.erb') -%> +<%- else -%> +<%= render('kotlin/android_class.erb') -%> +<%- end -%> diff --git a/codegen/lib/templates/kotlin_common.erb b/codegen/lib/templates/kotlin_common.erb new file mode 100644 index 00000000000..31db319babd --- /dev/null +++ b/codegen/lib/templates/kotlin_common.erb @@ -0,0 +1,7 @@ +<%- if entity.is_a?(EnumDecl) -%> +<%= render('kotlin/common_enum.erb') -%> +<%- elsif entity.is_struct -%> +<%= render('kotlin/common_struct.erb') -%> +<%- else -%> +<%= render('kotlin/common_class.erb') -%> +<%- end -%> diff --git a/codegen/lib/templates/kotlin_ios.erb b/codegen/lib/templates/kotlin_ios.erb new file mode 100644 index 00000000000..272ad2cf1df --- /dev/null +++ b/codegen/lib/templates/kotlin_ios.erb @@ -0,0 +1,7 @@ +<%- if entity.is_a?(EnumDecl) -%> +<%= render('kotlin/ios_enum.erb') -%> +<%- elsif entity.is_struct -%> +<%= render('kotlin/ios_struct.erb') -%> +<%- else -%> +<%= render('kotlin/ios_class.erb') -%> +<%- end -%> diff --git a/codegen/lib/templates/kotlin_jni/class_access.erb b/codegen/lib/templates/kotlin_jni/class_access.erb new file mode 100644 index 00000000000..a260b8cfd35 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/class_access.erb @@ -0,0 +1,6 @@ +<% param = locals[:param] -%> +<% name = param.name -%> +<% type = param.type -%> + jclass <%= name %>Class = (*env)->GetObjectClass(env, <%= name %>); + jfieldID <%= name %>HandleFieldID = (*env)->GetFieldID(env, <%= name %>Class, "nativeHandle", "J"); + struct TW<%= type.name %> *<%= name %>Instance = (struct TW<%= type.name %> *) (*env)->GetLongField(env, <%= name %>, <%= name %>HandleFieldID); diff --git a/codegen/lib/templates/kotlin_jni/compare_to.erb b/codegen/lib/templates/kotlin_jni/compare_to.erb new file mode 100644 index 00000000000..253a8584b6e --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/compare_to.erb @@ -0,0 +1,22 @@ +<% less = locals[:less] -%> +<% equal = locals[:equal] -%> +<% compareMethod = KotlinJniHelper.compareMethod(entity) -%> +<%= render('kotlin_jni/method_prototype.erb', { method: compareMethod }) %> { +<%= render('kotlin_jni/instance_access.erb', { entity: entity }) %> +<%= render('kotlin_jni/parameter_access.erb', { method: compareMethod }) -%> +<% if entity.struct? -%> + jboolean equal = (jboolean) TW<%= entity.name %>Equal(*instance, *otherInstance); +<% else -%> + jboolean equal = (jboolean) TW<%= entity.name %>Equal(instance, otherInstance); +<% end -%> + if (equal) { + return 0; + } +<% if entity.struct? -%> + jboolean less = (jboolean) TW<%= entity.name %>Less(*instance, *otherInstance); +<% else -%> + jboolean less = (jboolean) TW<%= entity.name %>Less(instance, otherInstance); +<% end -%> +<%= render('kotlin_jni/instance_release.erb', { entity: entity }) %> + return less ? -1 : 1; +} diff --git a/codegen/lib/templates/kotlin_jni/enum_access.erb b/codegen/lib/templates/kotlin_jni/enum_access.erb new file mode 100644 index 00000000000..43d612c0fa0 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/enum_access.erb @@ -0,0 +1,6 @@ +<% param = locals[:param] -%> +<% name = param.name -%> +<% type = param.type -%> + jclass <%= name %>Class = (*env)->GetObjectClass(env, <%= name %>); + jmethodID <%= name %>ValueMethodID = (*env)->GetMethodID(env, <%= name %>Class, "value", "()I"); + jint <%= name %>Value = (*env)->CallIntMethod(env, <%= name %>, <%= name %>ValueMethodID); diff --git a/codegen/lib/templates/kotlin_jni/instance_access.erb b/codegen/lib/templates/kotlin_jni/instance_access.erb new file mode 100644 index 00000000000..5654c472858 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/instance_access.erb @@ -0,0 +1,14 @@ +<% entity = locals[:entity] -%> + jclass thisClass = (*env)->GetObjectClass(env, thisObject); +<% if entity.struct? -%> + jfieldID bytesFieldID = (*env)->GetFieldID(env, thisClass, "bytes", "[B"); + jbyteArray bytesArray = (*env)->GetObjectField(env, thisObject, bytesFieldID); + jbyte* bytesBuffer = (*env)->GetByteArrayElements(env, bytesArray, NULL); + struct TW<%= entity.name %> *instance = (struct TW<%= entity.name %> *) bytesBuffer; +<% elsif entity.enum? -%> + jfieldID handleFieldID = (*env)->GetFieldID(env, thisClass, "value", "I"); + enum TW<%= entity.name %> instance = (enum TW<%= entity.name %>) (*env)->GetIntField(env, thisObject, handleFieldID); +<% else -%> + jfieldID handleFieldID = (*env)->GetFieldID(env, thisClass, "nativeHandle", "J"); + struct TW<%= entity.name %> *instance = (struct TW<%= entity.name %> *) (*env)->GetLongField(env, thisObject, handleFieldID); +<% end -%> diff --git a/codegen/lib/templates/kotlin_jni/instance_release.erb b/codegen/lib/templates/kotlin_jni/instance_release.erb new file mode 100644 index 00000000000..1231d03119b --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/instance_release.erb @@ -0,0 +1,6 @@ +<% entity = locals[:entity] -%> +<% if entity.struct? -%> + (*env)->ReleaseByteArrayElements(env, bytesArray, bytesBuffer, JNI_ABORT); + (*env)->DeleteLocalRef(env, bytesArray); +<% end -%> + (*env)->DeleteLocalRef(env, thisClass); \ No newline at end of file diff --git a/codegen/lib/templates/kotlin_jni/method.erb b/codegen/lib/templates/kotlin_jni/method.erb new file mode 100644 index 00000000000..f41b05435a8 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/method.erb @@ -0,0 +1,8 @@ +<% method = locals[:method] -%> +<%= render('kotlin_jni/method_prototype.erb', { method: method }) %> { +<% if !method.static -%> +<%= render('kotlin_jni/instance_access.erb', { entity: entity }) %> +<% end -%> +<%= render('kotlin_jni/parameter_access.erb', { method: method }) -%> +<%= render('kotlin_jni/method_forward.erb', { method: method }) -%> +} diff --git a/codegen/lib/templates/kotlin_jni/method_call.erb b/codegen/lib/templates/kotlin_jni/method_call.erb new file mode 100644 index 00000000000..68dfee4205a --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/method_call.erb @@ -0,0 +1,6 @@ +<% + method = locals[:method] + instance = (method.entity.struct? ? '*' : '') + 'instance' + arguments = locals[:arguments] || [instance] + KotlinJniHelper.arguments(method.parameters.drop(1)) +-%> +TW<%= entity.name %><%= method.name %>(<%= arguments.join(', ') %>) \ No newline at end of file diff --git a/codegen/lib/templates/kotlin_jni/method_forward.erb b/codegen/lib/templates/kotlin_jni/method_forward.erb new file mode 100644 index 00000000000..3673db58a48 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/method_forward.erb @@ -0,0 +1,118 @@ +<% + method = locals[:method] + if method.static + arguments = locals[:arguments] || KotlinJniHelper.arguments(method.parameters) + call = render('kotlin_jni/method_call.erb', { method: method, arguments: arguments }) + else + instance = (method.entity.struct? ? '*' : '') + 'instance' + arguments = locals[:arguments] || [instance] + KotlinJniHelper.arguments(method.parameters.drop(1)) + call = render('kotlin_jni/method_call.erb', { method: method, arguments: arguments }) + end + + # Method returns data + if should_return_data(method) -%> + <%= KotlinJniHelper.type(method.return_type) %> result = NULL; + TWData *resultData = <%= call %>; +<% if method.return_type.is_nullable %> + if (resultData == NULL) { + goto cleanup; + } +<% end -%> + result = TWDataJByteArray(resultData, env); +<% if method.return_type.is_nullable %> +cleanup: +<% end -%> +<%= render('kotlin_jni/parameter_release.erb', { method: method }) -%> +<% if !method.static %> +<%= render('kotlin_jni/instance_release.erb', { entity: entity }) %> +<% end -%> + return result; +<% + # Method returns a string + elsif should_return_string(method) -%> + jstring result = NULL; + TWString *resultString = <%= call %>; +<% if method.return_type.is_nullable %> + if (resultString == NULL) { + goto cleanup; + } +<% end -%> + result = TWStringJString(resultString, env); +<% if method.return_type.is_nullable %> +cleanup: +<% end -%> +<%= render('kotlin_jni/parameter_release.erb', { method: method }) -%> +<% if !method.static %> +<%= render('kotlin_jni/instance_release.erb', { entity: entity }) %> +<% end -%> + return result; +<% + # Method returns proto + elsif method.return_type.is_proto -%> + jbyteArray resultData = TWDataJByteArray(<%= call %>, env); + jclass resultClass = (*env)->FindClass(env, "<%= KotlinJniHelper.proto_to_class(method.return_type.name) %>"); + jmethodID parseFromMethodID = (*env)->GetStaticMethodID(env, resultClass, "parseFrom", "([B)L<%= KotlinJniHelper.proto_to_class(method.return_type.name) %>;"); + jobject result = (*env)->CallStaticObjectMethod(env, resultClass, parseFromMethodID, resultData); + + (*env)->DeleteLocalRef(env, resultClass); +<%= render('kotlin_jni/parameter_release.erb', { method: method }) -%> +<% if !method.static %> +<%= render('kotlin_jni/instance_release.erb', { entity: entity }) %> +<% end -%> + + return result; +<% + # Method returns an object + elsif method.return_type.is_struct || method.return_type.is_class || method.return_type.is_enum + if method.return_type.is_struct -%> + struct TW<%= method.return_type.name %> result = <%= call %>; +<% elsif method.return_type.is_class -%> + struct TW<%= method.return_type.name %> *result = <%= call %>; +<% elsif method.return_type.is_enum -%> + enum TW<%= method.return_type.name %> result = <%= call %>; +<% else -%> + TW<%= method.return_type.name %> *result = <%= call %>; +<% end -%> + +<%= render('kotlin_jni/parameter_release.erb', { method: method }) -%> +<% if !method.static %> +<%= render('kotlin_jni/instance_release.erb', { entity: entity }) %> +<% end -%> + + jclass class = (*env)->FindClass(env, "com/trustwallet/core/<%= method.return_type.name %>"); +<% if method.return_type.is_struct -%> + jbyteArray resultArray = (*env)->NewByteArray(env, sizeof(struct TW<%= method.return_type.name %>)); + (*env)->SetByteArrayRegion(env, resultArray, 0, sizeof(struct TW<%= method.return_type.name %>), (jbyte *) &result); + jmethodID method = (*env)->GetStaticMethodID(env, class, "createFromNative", "([B)Lcom/trustwallet/core/<%= method.return_type.name %>;"); + return (*env)->CallStaticObjectMethod(env, class, method, resultArray); +<% elsif method.return_type.is_enum -%> + jmethodID method = (*env)->GetStaticMethodID(env, class, "createFromValue", "(I)Lcom/trustwallet/core/<%= method.return_type.name %>;"); + return (*env)->CallStaticObjectMethod(env, class, method, (jint) result); +<% else -%> + if (result == NULL) { + return NULL; + } + jmethodID method = (*env)->GetStaticMethodID(env, class, "createFromNative", "(J)Lcom/trustwallet/core/<%= method.return_type.name %>;"); + return (*env)->CallStaticObjectMethod(env, class, method, (jlong) result); +<% end + + # Method returns void + elsif method.return_type.name == :void -%> + <%= call %>; + +<%= render('kotlin_jni/parameter_release.erb', { method: method }) -%> +<% if !method.static %> +<%= render('kotlin_jni/instance_release.erb', { entity: entity }) %> +<% end + + # Method returns a primitive + else -%> + <%= KotlinJniHelper.type(method.return_type) %> resultValue = (<%= KotlinJniHelper.type(method.return_type) %>) <%= call %>; + +<%= render('kotlin_jni/parameter_release.erb', { method: method }) -%> +<% if !method.static %> +<%= render('kotlin_jni/instance_release.erb', { entity: entity }) %> +<% end -%> + + return resultValue; +<%end -%> \ No newline at end of file diff --git a/codegen/lib/templates/kotlin_jni/method_prototype.erb b/codegen/lib/templates/kotlin_jni/method_prototype.erb new file mode 100644 index 00000000000..a971ce95bd5 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/method_prototype.erb @@ -0,0 +1,9 @@ +<% + method = locals[:method] + if method.static + parameters = 'jclass thisClass' + KotlinJniHelper.parameters(method.parameters) + else + parameters = 'jobject thisObject' + KotlinJniHelper.parameters(method.parameters.drop(1)) + end +-%> +<%= KotlinJniHelper.type(method.return_type) %> JNICALL <%= KotlinJniHelper.function_name(entity: entity, function: method) %>(JNIEnv *env, <%= parameters %>)<% -%> diff --git a/codegen/lib/templates/kotlin_jni/parameter_access.erb b/codegen/lib/templates/kotlin_jni/parameter_access.erb new file mode 100644 index 00000000000..8022686514b --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/parameter_access.erb @@ -0,0 +1,23 @@ +<% + method = locals[:method] + if method.static && !method.name.include?('Init') + parameters = method.parameters + else + parameters = method.parameters.drop(1) + end + + parameters.each do |param| + if param.type.name == :data -%> + TWData *<%= param.name %>Data = TWDataCreateWithJByteArray(env, <%= param.name %>); +<% elsif param.type.name == :string -%> + TWString *<%= param.name %>String = TWStringCreateWithJString(env, <%= param.name %>); +<% elsif param.type.is_struct -%> +<%= render('kotlin_jni/struct_access.erb', { param: param }) -%> +<% elsif param.type.is_class -%> +<%= render('kotlin_jni/class_access.erb', { param: param }) -%> +<% elsif param.type.is_enum -%> +<%= render('kotlin_jni/enum_access.erb', { param: param }) -%> +<% elsif param.type.is_proto -%> +<%= render('kotlin_jni/proto_access.erb', { param: param }) -%> +<% end -%> +<%end -%> diff --git a/codegen/lib/templates/kotlin_jni/parameter_release.erb b/codegen/lib/templates/kotlin_jni/parameter_release.erb new file mode 100644 index 00000000000..147e40c3496 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/parameter_release.erb @@ -0,0 +1,26 @@ +<% + method = locals[:method] + if method.static && !method.name.include?('Init') + parameters = method.parameters + else + parameters = method.parameters.drop(1) + end + + parameters.each do |param| + if param.type.name == :data -%> + TWDataDelete(<%= param.name %>Data); +<% elsif param.type.name == :string -%> + TWStringDelete(<%= param.name %>String); +<% elsif param.type.is_struct -%> + (*env)->ReleaseByteArrayElements(env, <%= param.name %>BytesArray, <%= param.name %>BytesBuffer, JNI_ABORT); + (*env)->DeleteLocalRef(env, <%= param.name %>BytesArray); + (*env)->DeleteLocalRef(env, <%= param.name %>Class); +<% elsif param.type.is_class -%> + (*env)->DeleteLocalRef(env, <%= param.name %>Class); +<% elsif param.type.is_enum -%> + (*env)->DeleteLocalRef(env, <%= param.name %>Class); +<% elsif param.type.is_proto -%> + (*env)->DeleteLocalRef(env, <%= param.name %>ByteArray); + (*env)->DeleteLocalRef(env, <%= param.name %>Class); +<% end -%> +<%end -%> diff --git a/codegen/lib/templates/kotlin_jni/proto_access.erb b/codegen/lib/templates/kotlin_jni/proto_access.erb new file mode 100644 index 00000000000..36d930a73cd --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/proto_access.erb @@ -0,0 +1,7 @@ +<% param = locals[:param] -%> +<% name = param.name -%> +<% type = param.type -%> + jclass <%= name %>Class = (*env)->GetObjectClass(env, <%= name %>); + jmethodID <%= name %>ToByteArrayMethodID = (*env)->GetMethodID(env, <%= name %>Class, "toByteArray", "()[B"); + jbyteArray <%= name %>ByteArray = (*env)->CallObjectMethod(env, <%= name %>, <%= name %>ToByteArrayMethodID); + TWData *<%= param.name %>Data = TWDataCreateWithJByteArray(env, <%= name %>ByteArray); diff --git a/codegen/lib/templates/kotlin_jni/struct_access.erb b/codegen/lib/templates/kotlin_jni/struct_access.erb new file mode 100644 index 00000000000..ed8c88c027b --- /dev/null +++ b/codegen/lib/templates/kotlin_jni/struct_access.erb @@ -0,0 +1,8 @@ +<% param = locals[:param] -%> +<% name = param.name -%> +<% type = param.type -%> + jclass <%= name %>Class = (*env)->GetObjectClass(env, <%= name %>); + jfieldID <%= name %>BytesFieldID = (*env)->GetFieldID(env, <%= name %>Class, "bytes", "[B"); + jbyteArray <%= name %>BytesArray = (*env)->GetObjectField(env, <%= name %>, <%= name %>BytesFieldID); + jbyte* <%= name %>BytesBuffer = (*env)->GetByteArrayElements(env, <%= name %>BytesArray, NULL); + struct TW<%= type.name %> *<%= name %>Instance = (struct TW<%= type.name %> *) <%= name %>BytesBuffer; diff --git a/codegen/lib/templates/kotlin_jni_c.erb b/codegen/lib/templates/kotlin_jni_c.erb new file mode 100644 index 00000000000..3b1f05b0588 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni_c.erb @@ -0,0 +1,90 @@ +#include +#include +#include + +<% require 'set' -%> +<% includes = Set.new([entity.name]) -%> +<% entity.static_methods.each do |method| -%> +<% includes << method.return_type.name if method.return_type.is_struct || method.return_type.is_class -%> +<% method.parameters.each do |param| -%> +<% includes << param.type.name if param.type.is_struct || param.type.is_class -%> +<% end -%> +<% end -%> +<% includes.each do |include| -%> +#include .h> +<% end -%> + +#include "TWJNI.h" +#include "<%= entity.name %>.h" + +<%# Constructors -%> +<% entity.static_methods.each do |method| -%> +<% next unless method.name.start_with?('Create') -%> +jlong JNICALL <%= KotlinJniHelper.function_name(entity: entity, function: method, native_prefix: true) %>(JNIEnv *env, jclass thisClass<%= KotlinJniHelper.parameters(method.parameters) %>) { +<%= render('kotlin_jni/parameter_access.erb', { method: method }) -%> + struct TW<%= entity.name %> *instance = TW<%= entity.name %><%= method.name %>(<%= KotlinJniHelper.arguments(method.parameters).join(', ') %>); +<%= render('kotlin_jni/parameter_release.erb', { method: method }) -%> + return (jlong) instance; +} + +<% end -%> +<%# Destructors -%> +<% entity.methods.each do |method| -%> +<% next unless method.name.start_with?('Delete') -%> +void JNICALL <%= KotlinJniHelper.function_name(entity: entity, function: method, native_prefix: true) %>(JNIEnv *env, jclass thisClass, jlong handle) { + TW<%= entity.name %>Delete((struct TW<%= entity.name %> *) handle); +} + +<% end -%> +<%# Initializers -%> +<% entity.static_methods.each do |method| -%> +<% next unless method.name.start_with?('Init') -%> +jbyteArray JNICALL <%= KotlinJniHelper.function_name(entity: entity, function: method) %>(JNIEnv *env, jclass thisClass<%= KotlinJniHelper.parameters(method.parameters.drop(1)) %>) { + jbyteArray array = (*env)->NewByteArray(env, sizeof(struct TW<%= entity.name %>)); + jbyte* bytesBuffer = (*env)->GetByteArrayElements(env, array, NULL); + struct TW<%= entity.name %> *instance = (struct TW<%= entity.name %> *) bytesBuffer; +<%= render('kotlin_jni/parameter_access.erb', { method: method }) -%> +<% if method.return_type.name != :void -%> + <%= KotlinJniHelper.type(method.return_type) %> result = (<%= KotlinJniHelper.type(method.return_type) %>) TW<%= entity.name %><%= method.name %>(instance, <%= KotlinJniHelper.arguments(method.parameters.drop(1)).join(', ') %>); +<% else -%> + TW<%= entity.name %><%= method.name %>(instance, <%= KotlinJniHelper.arguments(method.parameters.drop(1)).join(', ') %>); +<% end -%> +<%= render('kotlin_jni/parameter_release.erb', { method: method }) -%> + (*env)->ReleaseByteArrayElements(env, array, bytesBuffer, 0); + +<% if method.return_type.name != :void -%> + if (result) { + return array; + } else { + (*env)->DeleteLocalRef(env, array); + return NULL; + } +<% else -%> + return array; +<% end -%> +} + +<% end -%> +<%# Static properties -%> +<% entity.static_properties.each do |method| -%> +<%= render('kotlin_jni/method.erb', { method: method }) %> +<% end -%> +<%# Static methods -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') || method.name.start_with?('Init') -%> +<%= render('kotlin_jni/method.erb', { method: method }) %> +<% end -%> +<%# Properties -%> +<% entity.properties.each do |method| -%> +<%= render('kotlin_jni/method.erb', { method: method }) %> +<% end -%> +<%# Methods -%> +<% entity.methods.each do |method| -%> +<% next if method.name == "Delete" -%> +<%= render('kotlin_jni/method.erb', { method: method }) %> +<% end -%> +<% less = entity.static_methods.detect{ |i| i.name == 'Less' } -%> +<% equal = entity.static_methods.detect{ |i| i.name == 'Equal' } -%> +<% if !less.nil? && !equal.nil? -%> +<%= render('kotlin_jni/compare_to.erb', { less: less, equal: equal }) %> +<% end -%> diff --git a/codegen/lib/templates/kotlin_jni_h.erb b/codegen/lib/templates/kotlin_jni_h.erb new file mode 100644 index 00000000000..44e3bec9cf8 --- /dev/null +++ b/codegen/lib/templates/kotlin_jni_h.erb @@ -0,0 +1,66 @@ +#ifndef JNI_TW_<%= entity.name.upcase %>_H +#define JNI_TW_<%= entity.name.upcase %>_H + +#include +#include + +TW_EXTERN_C_BEGIN + +<%# Constructor declarations -%> +<% entity.static_methods.each do |method| -%> +<% next unless method.name.start_with?('Create') -%> +JNIEXPORT +jlong JNICALL <%= KotlinJniHelper.function_name(entity: entity, function: method, native_prefix: true) %>(JNIEnv *env, jclass thisClass<%= KotlinJniHelper.parameters(method.parameters) %>); + +<% end -%> +<%# Destructor declarations -%> +<% entity.methods.each do |method| -%> +<% next unless method.name.start_with?('Delete') -%> +JNIEXPORT +void JNICALL <%= KotlinJniHelper.function_name(entity: entity, function: method, native_prefix: true) %>(JNIEnv *env, jclass thisClass, jlong handle); + +<% end -%> +<%# Initializer declarations -%> +<% entity.static_methods.each do |method| -%> +<% next unless method.name.start_with?('Init') -%> +JNIEXPORT +jbyteArray JNICALL <%= KotlinJniHelper.function_name(entity: entity, function: method) %>(JNIEnv *env, jclass thisClass<%= KotlinJniHelper.parameters(method.parameters.drop(1)) %>); + +<% end -%> +<%# Static property declarations -%> +<% entity.static_properties.each do |property| -%> +JNIEXPORT +<%= render('kotlin_jni/method_prototype.erb', { method: property }) %>; + +<% end -%> +<%# Static method declarations -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') || method.name.start_with?('Init') -%> +JNIEXPORT +<%= render('kotlin_jni/method_prototype.erb', { method: method }) %>; + +<% end -%> +<%# Property declarations -%> +<% entity.properties.each do |property| -%> +JNIEXPORT +<%= render('kotlin_jni/method_prototype.erb', { method: property }) %>; + +<% end -%> +<%# Method declarations -%> +<% entity.methods.each do |method| -%> +<% next if method.name.start_with?('Delete') -%> +JNIEXPORT +<%= render('kotlin_jni/method_prototype.erb', { method: method }) %>; + +<% end -%> +<% less = entity.static_methods.detect{ |i| i.name == 'Less' } -%> +<% equal = entity.static_methods.detect{ |i| i.name == 'Equal' } -%> +<% if !less.nil? && !equal.nil? -%> +JNIEXPORT +<%= render('kotlin_jni/method_prototype.erb', { method: KotlinJniHelper.compareMethod(entity) }) %>; + +<% end -%> + +TW_EXTERN_C_END + +#endif // JNI_TW_<%= entity.name.upcase %>_H diff --git a/codegen/lib/templates/kotlin_js.erb b/codegen/lib/templates/kotlin_js.erb new file mode 100644 index 00000000000..db9c23dd567 --- /dev/null +++ b/codegen/lib/templates/kotlin_js.erb @@ -0,0 +1,7 @@ +<%- if entity.is_a?(EnumDecl) -%> +<%= render('kotlin/js_enum.erb') -%> +<%- elsif entity.is_struct -%> +<%= render('kotlin/js_struct.erb') -%> +<%- else -%> +<%= render('kotlin/js_class.erb') -%> +<%- end -%> diff --git a/jni/cpp/AnySigner.c b/jni/android/AnySigner.c similarity index 100% rename from jni/cpp/AnySigner.c rename to jni/android/AnySigner.c diff --git a/jni/cpp/AnySigner.h b/jni/android/AnySigner.h similarity index 100% rename from jni/cpp/AnySigner.h rename to jni/android/AnySigner.h diff --git a/jni/cpp/Random.cpp b/jni/cpp/Random.cpp index de97baff225..3e15e28062f 100644 --- a/jni/cpp/Random.cpp +++ b/jni/cpp/Random.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -27,7 +27,7 @@ uint32_t random32() { void random_buffer(uint8_t *buf, size_t len) { JNIEnv *env; - cachedJVM->AttachCurrentThread(&env, NULL); + cachedJVM->AttachCurrentThread(&env, nullptr); // SecureRandom random = new SecureRandom(); jclass secureRandomClass = env->FindClass("java/security/SecureRandom"); diff --git a/jni/cpp/TWJNI.h b/jni/cpp/TWJNI.h index 29bf14ba4df..4fc8d55d71f 100644 --- a/jni/cpp/TWJNI.h +++ b/jni/cpp/TWJNI.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/jni/cpp/TWJNIData.cpp b/jni/cpp/TWJNIData.cpp index c0577f31442..47e294f64b0 100644 --- a/jni/cpp/TWJNIData.cpp +++ b/jni/cpp/TWJNIData.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,7 +10,7 @@ #include "TWJNIData.h" jbyteArray TWDataJByteArray(TWData *_Nonnull data, JNIEnv *env) { - jsize dataSize = static_cast(TWDataSize(data)); + auto dataSize = static_cast(TWDataSize(data)); jbyteArray array = env->NewByteArray(dataSize); env->SetByteArrayRegion(array, 0, dataSize, (jbyte *) TWDataBytes(data)); TWDataDelete(data); diff --git a/jni/cpp/TWJNIData.h b/jni/cpp/TWJNIData.h index a7002bffce5..39d3ca47aa9 100644 --- a/jni/cpp/TWJNIData.h +++ b/jni/cpp/TWJNIData.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/jni/cpp/TWJNIString.cpp b/jni/cpp/TWJNIString.cpp index 2bb220fb6fc..95b017fac2e 100644 --- a/jni/cpp/TWJNIString.cpp +++ b/jni/cpp/TWJNIString.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -15,8 +15,8 @@ jstring _Nonnull TWStringJString(TWString *_Nonnull string, JNIEnv *env) { } TWString *_Nonnull TWStringCreateWithJString(JNIEnv *env, jstring _Nonnull string) { - auto chars = env->GetStringUTFChars(string, nullptr); - auto twstring = TWStringCreateWithUTF8Bytes(chars); + const auto *chars = env->GetStringUTFChars(string, nullptr); + const auto *twstring = TWStringCreateWithUTF8Bytes(chars); env->ReleaseStringUTFChars(string, chars); return twstring; } diff --git a/jni/cpp/TWJNIString.h b/jni/cpp/TWJNIString.h index 95f0f967898..e6452df0134 100644 --- a/jni/cpp/TWJNIString.h +++ b/jni/cpp/TWJNIString.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/jni/kotlin/AnySigner.c b/jni/kotlin/AnySigner.c new file mode 100644 index 00000000000..8e0c9f389f8 --- /dev/null +++ b/jni/kotlin/AnySigner.c @@ -0,0 +1,56 @@ +// Copyright © 2017-2023 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 +#include +#include + +#include "AnySigner.h" +#include "TWJNI.h" + +jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_signImpl(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin) { + jclass coinClass = (*env)->GetObjectClass(env, coin); + jmethodID coinValueMethodID = (*env)->GetMethodID(env, coinClass, "value", "()I"); + uint32_t coinValue = (*env)->CallIntMethod(env, coin, coinValueMethodID); + + TWData *inputData = TWDataCreateWithJByteArray(env, input); + TWData *outputData = TWAnySignerSign(inputData, coinValue); + jbyteArray resultData = TWDataJByteArray(outputData, env); + TWDataDelete(inputData); + return resultData; +} + +jboolean JNICALL Java_com_trustwallet_core_AnySignerKt_supportsJsonImpl(JNIEnv *env, jclass thisClass, jobject coin) { + jclass coinClass = (*env)->GetObjectClass(env, coin); + jmethodID coinValueMethodID = (*env)->GetMethodID(env, coinClass, "value", "()I"); + uint32_t coinValue = (*env)->CallIntMethod(env, coin, coinValueMethodID); + return TWAnySignerSupportsJSON(coinValue); +} + +jstring JNICALL Java_com_trustwallet_core_AnySignerKt_signJsonImpl(JNIEnv *env, jclass thisClass, jstring json, jbyteArray key, jobject coin) { + jclass coinClass = (*env)->GetObjectClass(env, coin); + jmethodID coinValueMethodID = (*env)->GetMethodID(env, coinClass, "value", "()I"); + uint32_t coinValue = (*env)->CallIntMethod(env, coin, coinValueMethodID); + + TWString *jsonString = TWStringCreateWithJString(env, json); + TWData *keyData = TWDataCreateWithJByteArray(env, key); + TWString *result = TWAnySignerSignJSON(jsonString, keyData, coinValue); + TWDataDelete(keyData); + TWStringDelete(jsonString); + return TWStringJString(result, env); +} + +jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_planImpl(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin) { + jclass coinClass = (*env)->GetObjectClass(env, coin); + jmethodID coinValueMethodID = (*env)->GetMethodID(env, coinClass, "value", "()I"); + uint32_t coinValue = (*env)->CallIntMethod(env, coin, coinValueMethodID); + + TWData *inputData = TWDataCreateWithJByteArray(env, input); + TWData *outputData = TWAnySignerPlan(inputData, coinValue); + jbyteArray resultData = TWDataJByteArray(outputData, env); + TWDataDelete(inputData); + return resultData; +} diff --git a/jni/kotlin/AnySigner.h b/jni/kotlin/AnySigner.h new file mode 100644 index 00000000000..18f98104f62 --- /dev/null +++ b/jni/kotlin/AnySigner.h @@ -0,0 +1,29 @@ +// Copyright © 2017-2023 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. + +#ifndef JNI_TW_ANYSIGNER_H +#define JNI_TW_ANYSIGNER_H + +#include +#include + +TW_EXTERN_C_BEGIN + +JNIEXPORT +jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_signImpl(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin); + +JNIEXPORT +jboolean JNICALL Java_com_trustwallet_core_AnySignerKt_supportsJsonImpl(JNIEnv *env, jclass thisClass, jobject coin); + +JNIEXPORT +jstring JNICALL Java_com_trustwallet_core_AnySignerKt_signJsonImpl(JNIEnv *env, jclass thisClass, jstring json, jbyteArray key, jobject coin); + +JNIEXPORT +jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_planImpl(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin); + +TW_EXTERN_C_END + +#endif // JNI_TW_ANYSIGNER_H diff --git a/kotlin/.editorconfig b/kotlin/.editorconfig new file mode 100644 index 00000000000..aff6d71fbe4 --- /dev/null +++ b/kotlin/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 + +[*.{kt,kts}] +indent_size = 4 +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL +ij_kotlin_imports_layout = * +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true +ij_kotlin_enum_constants_wrap = split_into_lines +ij_kotlin_packages_to_use_import_on_demand = * +ij_kotlin_wrap_first_method_in_call_chain = true diff --git a/kotlin/.gitignore b/kotlin/.gitignore new file mode 100644 index 00000000000..f8ed7018916 --- /dev/null +++ b/kotlin/.gitignore @@ -0,0 +1,2 @@ +.gradle +local.properties diff --git a/kotlin/README.md b/kotlin/README.md new file mode 100644 index 00000000000..42e4cf78a39 --- /dev/null +++ b/kotlin/README.md @@ -0,0 +1,5 @@ +### Tasks: + +- `./gradlew :wallet-core-kotlin:generateProtos` – Generates Kotlin classes for Protos +- `./gradlew :wallet-core-kotlin:generateCinterop` – Generates def file +- `./gradlew :wallet-core-kotlin:generateFiles` – Generates all the above and Kotlin accessors to the library diff --git a/kotlin/build-logic/build.gradle.kts b/kotlin/build-logic/build.gradle.kts new file mode 100644 index 00000000000..d6cda1875d1 --- /dev/null +++ b/kotlin/build-logic/build.gradle.kts @@ -0,0 +1,14 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `kotlin-dsl` +} + +allprojects { + tasks.withType { + kotlinOptions { + allWarningsAsErrors = true + jvmTarget = JavaVersion.VERSION_11.toString() + } + } +} diff --git a/kotlin/build-logic/settings.gradle.kts b/kotlin/build-logic/settings.gradle.kts new file mode 100644 index 00000000000..5cab7b66872 --- /dev/null +++ b/kotlin/build-logic/settings.gradle.kts @@ -0,0 +1,23 @@ +@file:Suppress("UnstableApiUsage") + +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } + + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/kotlin/build-logic/src/main/kotlin/convention.file-generation.gradle.kts b/kotlin/build-logic/src/main/kotlin/convention.file-generation.gradle.kts new file mode 100644 index 00000000000..028fb3a9c2f --- /dev/null +++ b/kotlin/build-logic/src/main/kotlin/convention.file-generation.gradle.kts @@ -0,0 +1,86 @@ +val libs = extensions.getByType().named("libs") + +val generateCinteropTask = task("generateCinterop") { + doFirst { + val headersDir = rootDir.parentFile.resolve("include/TrustWalletCore") + val headers = headersDir + .listFiles { file -> file.extension == "h" } + .orEmpty() + .sortedBy { it.name } + .joinToString(separator = " ") { it.name } + + val defFile = projectDir.resolve("src/nativeInterop/cinterop/walletCore.def") + defFile.parentFile.mkdirs() + defFile.writeText( + text = + """ + headers = $headers + package = com.trustwallet.core + + """.trimIndent(), + ) + } +} + +val copyProtoTask = task("copyProtos") { + val sourceDir = rootDir.parentFile.resolve("src/proto") + val destinationDir = projectDir.resolve("build/tmp/proto") + + doFirst { + destinationDir.deleteRecursively() + } + + from(sourceDir) { + include("*.proto") + } + into(destinationDir) + + doLast { + destinationDir + .listFiles { file -> file.extension == "proto" } + .orEmpty() + .forEach { file -> + val packageName = file.nameWithoutExtension.toLowerCase() + file + .readText() + .replaceFirst( + oldValue = """option java_package = "wallet.core.jni.proto";""", + newValue = """option java_package = "com.trustwallet.core.$packageName";""", + ) + .let { file.writeText(it) } + } + } +} + +val wire: Configuration by configurations.creating +dependencies { + wire(libs.findLibrary("wire.compiler").get().get()) +} + +val generateProtosTask = task("generateProtos") { + dependsOn(copyProtoTask) + + val sourceDir = projectDir.resolve("build/tmp/proto") + val destinationDir = projectDir.resolve("src/commonMain/proto") + + doFirst { + destinationDir.deleteRecursively() + destinationDir.mkdirs() + } + + mainClass.set("com.squareup.wire.WireCompiler") + classpath = wire + + args( + "--proto_path=$sourceDir", + "--kotlin_out=$destinationDir", + ) +} + +task("generateFiles") { + dependsOn(generateCinteropTask) + dependsOn(generateProtosTask) + + workingDir(rootDir.parentFile) + commandLine("./codegen/bin/codegen") +} diff --git a/kotlin/build-logic/src/main/kotlin/convention.maven-publish.gradle.kts b/kotlin/build-logic/src/main/kotlin/convention.maven-publish.gradle.kts new file mode 100644 index 00000000000..37072540d89 --- /dev/null +++ b/kotlin/build-logic/src/main/kotlin/convention.maven-publish.gradle.kts @@ -0,0 +1,21 @@ +plugins { + `maven-publish` +} + +group = "com.trustwallet" +if (version == Project.DEFAULT_VERSION) { + version = "0.0.0-alpha" +} + +publishing { + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/trustwallet/wallet-core") + credentials { + username = System.getenv("GITHUB_USER") + password = System.getenv("GITHUB_TOKEN") + } + } + } +} diff --git a/kotlin/build.gradle.kts b/kotlin/build.gradle.kts new file mode 100644 index 00000000000..87279b47a55 --- /dev/null +++ b/kotlin/build.gradle.kts @@ -0,0 +1,20 @@ +// Workaround https://github.com/gradle/gradle/issues/22797 +@file:Suppress("DSL_SCOPE_VIOLATION") + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("com.android.application") version libs.versions.agp.get() apply false + id("com.android.library") version libs.versions.agp.get() apply false + kotlin("android") version libs.versions.kotlin.get() apply false + kotlin("multiplatform") version libs.versions.kotlin.get() apply false +} + +allprojects { + tasks.withType { + kotlinOptions { + allWarningsAsErrors = true + jvmTarget = JavaVersion.VERSION_11.toString() + } + } +} diff --git a/kotlin/gradle.properties b/kotlin/gradle.properties new file mode 100644 index 00000000000..d1b2beb446c --- /dev/null +++ b/kotlin/gradle.properties @@ -0,0 +1,12 @@ +# Gradle +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 +org.gradle.parallel=true +# Kotlin +kotlin.code.style=official +kotlin.js.compiler=ir +kotlin.mpp.androidSourceSetLayoutVersion=2 +kotlin.mpp.enableCInteropCommonization=true +# Android +android.useAndroidX=true +android.nonTransitiveRClass=true +android.disableAutomaticComponentCreation=true diff --git a/kotlin/gradle/libs.versions.toml b/kotlin/gradle/libs.versions.toml new file mode 100644 index 00000000000..012e8c6ba34 --- /dev/null +++ b/kotlin/gradle/libs.versions.toml @@ -0,0 +1,14 @@ +[versions] +android-sdk-tools = "33.0.1" +android-sdk-min = "24" +android-sdk-compile = "33" +android-cmake = "3.22.1" +android-ndk = "25.2.9519653" + +kotlin = "1.8.10" +agp = "7.4.1" +wire = "4.4.3" + +[libraries] +wire-runtime = { module = "com.squareup.wire:wire-runtime", version.ref = "wire" } +wire-compiler = { module = "com.squareup.wire:wire-compiler", version.ref = "wire" } diff --git a/kotlin/gradle/wrapper/gradle-wrapper.jar b/kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..943f0cbfa754578e88a3dae77fce6e3dea56edbf GIT binary patch literal 61574 zcmb6AV{~QRwml9f72CFLyJFk6ZKq;e729@pY}>YNR8p1vbMJH7ubt# zZR`2@zJD1Ad^Oa6Hk1{VlN1wGR-u;_dyt)+kddaNpM#U8qn@6eX;fldWZ6BspQIa= zoRXcQk)#ENJ`XiXJuK3q0$`Ap92QXrW00Yv7NOrc-8ljOOOIcj{J&cR{W`aIGXJ-` z`ez%Mf7qBi8JgIb{-35Oe>Zh^GIVe-b^5nULQhxRDZa)^4+98@`hUJe{J%R>|LYHA z4K3~Hjcp8_owGF{d~lZVKJ;kc48^OQ+`_2migWY?JqgW&))70RgSB6KY9+&wm<*8 z_{<;(c;5H|u}3{Y>y_<0Z59a)MIGK7wRMX0Nvo>feeJs+U?bt-++E8bu7 zh#_cwz0(4#RaT@xy14c7d<92q-Dd}Dt<*RS+$r0a^=LGCM{ny?rMFjhgxIG4>Hc~r zC$L?-FW0FZ((8@dsowXlQq}ja%DM{z&0kia*w7B*PQ`gLvPGS7M}$T&EPl8mew3In z0U$u}+bk?Vei{E$6dAYI8Tsze6A5wah?d(+fyP_5t4ytRXNktK&*JB!hRl07G62m_ zAt1nj(37{1p~L|m(Bsz3vE*usD`78QTgYIk zQ6BF14KLzsJTCqx&E!h>XP4)bya|{*G7&T$^hR0(bOWjUs2p0uw7xEjbz1FNSBCDb@^NIA z$qaq^0it^(#pFEmuGVS4&-r4(7HLmtT%_~Xhr-k8yp0`$N|y>#$Ao#zibzGi*UKzi zhaV#@e1{2@1Vn2iq}4J{1-ox;7K(-;Sk{3G2_EtV-D<)^Pk-G<6-vP{W}Yd>GLL zuOVrmN@KlD4f5sVMTs7c{ATcIGrv4@2umVI$r!xI8a?GN(R;?32n0NS(g@B8S00-=zzLn z%^Agl9eV(q&8UrK^~&$}{S(6-nEXnI8%|hoQ47P?I0Kd=woZ-pH==;jEg+QOfMSq~ zOu>&DkHsc{?o&M5`jyJBWbfoPBv9Y#70qvoHbZXOj*qRM(CQV=uX5KN+b>SQf-~a8 ziZg}@&XHHXkAUqr)Q{y`jNd7`1F8nm6}n}+_She>KO`VNlnu(&??!(i#$mKOpWpi1 z#WfWxi3L)bNRodhPM~~?!5{TrrBY_+nD?CIUupkwAPGz-P;QYc-DcUoCe`w(7)}|S zRvN)9ru8b)MoullmASwsgKQo1U6nsVAvo8iKnbaWydto4y?#-|kP^%e6m@L`88KyDrLH`=EDx*6>?r5~7Iv~I zr__%SximG(izLKSnbTlXa-ksH@R6rvBrBavt4)>o3$dgztLt4W=!3=O(*w7I+pHY2(P0QbTma+g#dXoD7N#?FaXNQ^I0*;jzvjM}%=+km`YtC%O#Alm| zqgORKSqk!#^~6whtLQASqiJ7*nq?38OJ3$u=Tp%Y`x^eYJtOqTzVkJ60b2t>TzdQ{I}!lEBxm}JSy7sy8DpDb zIqdT%PKf&Zy--T^c-;%mbDCxLrMWTVLW}c=DP2>Td74)-mLl|70)8hU??(2)I@Zyo z2i`q5oyA!!(2xV~gahuKl&L(@_3SP012#x(7P!1}6vNFFK5f*A1xF({JwxSFwA|TM z&1z}!*mZKcUA-v4QzLz&5wS$7=5{M@RAlx@RkJaA4nWVqsuuaW(eDh^LNPPkmM~Al zwxCe@*-^4!ky#iNv2NIIU$CS+UW%ziW0q@6HN3{eCYOUe;2P)C*M`Bt{~-mC%T3%# zEaf)lATO1;uF33x>Hr~YD0Ju*Syi!Jz+x3myVvU^-O>C*lFCKS&=Tuz@>&o?68aF& zBv<^ziPywPu#;WSlTkzdZ9`GWe7D8h<1-v0M*R@oYgS5jlPbgHcx)n2*+!+VcGlYh?;9Ngkg% z=MPD+`pXryN1T|%I7c?ZPLb3bqWr7 zU4bfG1y+?!bw)5Iq#8IqWN@G=Ru%Thxf)#=yL>^wZXSCC8we@>$hu=yrU;2=7>h;5 zvj_pYgKg2lKvNggl1ALnsz2IlcvL;q79buN5T3IhXuJvy@^crqWpB-5NOm{7UVfxmPJ>`?;Tn@qHzF+W!5W{8Z&ZAnDOquw6r4$bv*jM#5lc%3v|c~^ zdqo4LuxzkKhK4Q+JTK8tR_|i6O(x#N2N0Fy5)!_trK&cn9odQu#Vlh1K~7q|rE z61#!ZPZ+G&Y7hqmY;`{XeDbQexC2@oFWY)Nzg@lL3GeEVRxWQlx@0?Zt`PcP0iq@6 zLgc)p&s$;*K_;q0L(mQ8mKqOJSrq$aQYO-Hbssf3P=wC6CvTVHudzJH-Jgm&foBSy zx0=qu$w477lIHk);XhaUR!R-tQOZ;tjLXFH6;%0)8^IAc*MO>Q;J={We(0OHaogG0 zE_C@bXic&m?F7slFAB~x|n#>a^@u8lu;=!sqE*?vq zu4`(x!Jb4F#&3+jQ|ygldPjyYn#uCjNWR)%M3(L!?3C`miKT;~iv_)dll>Q6b+I&c zrlB04k&>mSYLR7-k{Od+lARt~3}Bv!LWY4>igJl!L5@;V21H6dNHIGr+qV551e@yL z`*SdKGPE^yF?FJ|`#L)RQ?LJ;8+={+|Cl<$*ZF@j^?$H%V;jqVqt#2B0yVr}Nry5R z5D?S9n+qB_yEqvdy9nFc+8WxK$XME$3ftSceLb+L(_id5MMc*hSrC;E1SaZYow%jh zPgo#1PKjE+1QB`Of|aNmX?}3TP;y6~0iN}TKi3b+yvGk;)X&i3mTnf9M zuv3qvhErosfZ%Pb-Q>|BEm5(j-RV6Zf^$icM=sC-5^6MnAvcE9xzH@FwnDeG0YU{J zi~Fq?=bi0;Ir=hfOJu8PxC)qjYW~cv^+74Hs#GmU%Cw6?3LUUHh|Yab`spoqh8F@_ zm4bCyiXPx-Cp4!JpI~w!ShPfJOXsy>f*|$@P8L8(oeh#~w z-2a4IOeckn6}_TQ+rgl_gLArS3|Ml(i<`*Lqv6rWh$(Z5ycTYD#Z*&-5mpa}a_zHt z6E`Ty-^L9RK-M*mN5AasoBhc|XWZ7=YRQSvG)3$v zgr&U_X`Ny0)IOZtX}e$wNUzTpD%iF7Rgf?nWoG2J@PsS-qK4OD!kJ?UfO+1|F*|Bo z1KU`qDA^;$0*4mUJ#{EPOm7)t#EdX=Yx1R2T&xlzzThfRC7eq@pX&%MO&2AZVO%zw zS;A{HtJiL=rfXDigS=NcWL-s>Rbv|=)7eDoOVnVI>DI_8x>{E>msC$kXsS}z?R6*x zi(yO`$WN)_F1$=18cbA^5|f`pZA+9DG_Zu8uW?rA9IxUXx^QCAp3Gk1MSdq zBZv;_$W>*-zLL)F>Vn`}ti1k!%6{Q=g!g1J*`KONL#)M{ZC*%QzsNRaL|uJcGB7jD zTbUe%T(_x`UtlM!Ntp&-qu!v|mPZGcJw$mdnanY3Uo>5{oiFOjDr!ZznKz}iWT#x& z?*#;H$`M0VC|a~1u_<(}WD>ogx(EvF6A6S8l0%9U<( zH||OBbh8Tnzz*#bV8&$d#AZNF$xF9F2{_B`^(zWNC}af(V~J+EZAbeC2%hjKz3V1C zj#%d%Gf(uyQ@0Y6CcP^CWkq`n+YR^W0`_qkDw333O<0FoO9()vP^!tZ{`0zsNQx~E zb&BcBU>GTP2svE2Tmd;~73mj!_*V8uL?ZLbx}{^l9+yvR5fas+w&0EpA?_g?i9@A$j*?LnmctPDQG|zJ`=EF}Vx8aMD^LrtMvpNIR*|RHA`ctK*sbG= zjN7Q)(|dGpC}$+nt~bupuKSyaiU}Ws{?Tha@$q}cJ;tvH>+MuPih+B4d$Zbq9$Y*U z)iA(-dK?Ov@uCDq48Zm%%t5uw1GrnxDm7*ITGCEF!2UjA`BqPRiUR`yNq^zz|A3wU zG(8DAnY-GW+PR2&7@In{Sla(XnMz5Rk^*5u4UvCiDQs@hvZXoiziv{6*i?fihVI|( zPrY8SOcOIh9-AzyJ*wF4hq%ojB&Abrf;4kX@^-p$mmhr}xxn#fVU?ydmD=21&S)s*v*^3E96(K1}J$6bi8pyUr-IU)p zcwa$&EAF$0Aj?4OYPcOwb-#qB=kCEDIV8%^0oa567_u6`9+XRhKaBup z2gwj*m#(}=5m24fBB#9cC?A$4CCBj7kanaYM&v754(b%Vl!gg&N)ZN_gO0mv(jM0# z>FC|FHi=FGlEt6Hk6H3!Yc|7+q{&t%(>3n#>#yx@*aS+bw)(2!WK#M0AUD~wID>yG z?&{p66jLvP1;!T7^^*_9F322wJB*O%TY2oek=sA%AUQT75VQ_iY9`H;ZNKFQELpZd z$~M`wm^Y>lZ8+F0_WCJ0T2td`bM+b`)h3YOV%&@o{C#|t&7haQfq#uJJP;81|2e+$ z|K#e~YTE87s+e0zCE2X$df`o$`8tQhmO?nqO?lOuTJ%GDv&-m_kP9X<5GCo1=?+LY z?!O^AUrRb~3F!k=H7Aae5W0V1{KlgH379eAPTwq=2+MlNcJ6NM+4ztXFTwI)g+)&Q7G4H%KH_(}1rq%+eIJ*3$?WwnZxPZ;EC=@`QS@|-I zyl+NYh&G>k%}GL}1;ap8buvF>x^yfR*d+4Vkg7S!aQ++_oNx6hLz6kKWi>pjWGO5k zlUZ45MbA=v(xf>Oeqhg8ctl56y{;uDG?A9Ga5aEzZB80BW6vo2Bz&O-}WAq>(PaV;*SX0=xXgI_SJ< zYR&5HyeY%IW}I>yKu^?W2$~S!pw?)wd4(#6;V|dVoa}13Oiz5Hs6zA zgICc;aoUt$>AjDmr0nCzeCReTuvdD1{NzD1wr*q@QqVW*Wi1zn;Yw1dSwLvTUwg#7 zpp~Czra7U~nSZZTjieZxiu~=}!xgV68(!UmQz@#w9#$0Vf@y%!{uN~w^~U_d_Aa&r zt2l>)H8-+gA;3xBk?ZV2Cq!L71;-tb%7A0FWziYwMT|#s_Ze_B>orZQWqDOZuT{|@ zX04D%y&8u@>bur&*<2??1KnaA7M%%gXV@C3YjipS4|cQH68OSYxC`P#ncvtB%gnEI z%fxRuH=d{L70?vHMi>~_lhJ@MC^u#H66=tx?8{HG;G2j$9@}ZDYUuTetwpvuqy}vW)kDmj^a|A%z(xs7yY2mU0#X2$un&MCirr|7 z%m?8+9aekm0x5hvBQ2J+>XeAdel$cy>J<6R3}*O^j{ObSk_Ucv$8a3_WPTd5I4HRT z(PKP5!{l*{lk_19@&{5C>TRV8_D~v*StN~Pm*(qRP+`1N12y{#w_fsXrtSt={0hJw zQ(PyWgA;;tBBDql#^2J(pnuv;fPn(H>^d<6BlI%00ylJZ?Evkh%=j2n+|VqTM~EUh zTx|IY)W;3{%x(O{X|$PS&x0?z#S2q-kW&G}7#D?p7!Q4V&NtA_DbF~v?cz6_l+t8e zoh1`dk;P-%$m(Ud?wnoZn0R=Ka$`tnZ|yQ-FN!?!9Wmb^b(R!s#b)oj9hs3$p%XX9DgQcZJE7B_dz0OEF6C zx|%jlqj0WG5K4`cVw!19doNY+(;SrR_txAlXxf#C`uz5H6#0D>SzG*t9!Fn|^8Z8; z1w$uiQzufUzvPCHXhGma>+O327SitsB1?Rn6|^F198AOx}! zfXg22Lm0x%=gRvXXx%WU2&R!p_{_1H^R`+fRO2LT%;He@yiekCz3%coJ=8+Xbc$mN zJ;J7*ED|yKWDK3CrD?v#VFj|l-cTgtn&lL`@;sMYaM1;d)VUHa1KSB5(I54sBErYp z>~4Jz41?Vt{`o7T`j=Se{-kgJBJG^MTJ}hT00H%U)pY-dy!M|6$v+-d(CkZH5wmo1 zc2RaU`p3_IJ^hf{g&c|^;)k3zXC0kF1>rUljSxd}Af$!@@R1fJWa4g5vF?S?8rg=Z z4_I!$dap>3l+o|fyYy(sX}f@Br4~%&&#Z~bEca!nMKV zgQSCVC!zw^j<61!7#T!RxC6KdoMNONcM5^Q;<#~K!Q?-#6SE16F*dZ;qv=`5 z(kF|n!QIVd*6BqRR8b8H>d~N@ab+1+{3dDVPVAo>{mAB#m&jX{usKkCg^a9Fef`tR z?M79j7hH*;iC$XM)#IVm&tUoDv!(#f=XsTA$)(ZE37!iu3Gkih5~^Vlx#<(M25gr@ zOkSw4{l}6xI(b0Gy#ywglot$GnF)P<FQt~9ge1>qp8Q^k;_Dm1X@Tc^{CwYb4v_ld}k5I$&u}avIDQ-D(_EP zhgdc{)5r_iTFiZ;Q)5Uq=U73lW%uYN=JLo#OS;B0B=;j>APk?|!t{f3grv0nv}Z%` zM%XJk^#R69iNm&*^0SV0s9&>cl1BroIw*t3R0()^ldAsq)kWcI=>~4!6fM#0!K%TS ziZH=H%7-f=#-2G_XmF$~Wl~Um%^9%AeNSk)*`RDl##y+s)$V`oDlnK@{y+#LNUJp1^(e89sed@BB z^W)sHm;A^9*RgQ;f(~MHK~bJRvzezWGr#@jYAlXIrCk_iiUfC_FBWyvKj2mBF=FI;9|?0_~=E<)qnjLg9k*Qd!_ zl}VuSJB%#M>`iZm*1U^SP1}rkkI};91IRpZw%Hb$tKmr6&H5~m?A7?+uFOSnf)j14 zJCYLOYdaRu>zO%5d+VeXa-Ai7{7Z}iTn%yyz7hsmo7E|{ z@+g9cBcI-MT~2f@WrY0dpaC=v{*lDPBDX}OXtJ|niu$xyit;tyX5N&3pgmCxq>7TP zcOb9%(TyvOSxtw%Y2+O&jg39&YuOtgzn`uk{INC}^Na_-V;63b#+*@NOBnU{lG5TS zbC+N-qt)u26lggGPcdrTn@m+m>bcrh?sG4b(BrtdIKq3W<%?WuQtEW0Z)#?c_Lzqj*DlZ zVUpEV3~mG#DN$I#JJp3xc8`9ex)1%Il7xKwrpJt)qtpq}DXqI=5~~N}N?0g*YwETZ z(NKJO5kzh?Os`BQ7HYaTl>sXVr!b8>(Wd&PU*3ivSn{;q`|@n*J~-3tbm;4WK>j3&}AEZ*`_!gJ3F4w~4{{PyLZklDqWo|X}D zbZU_{2E6^VTCg#+6yJt{QUhu}uMITs@sRwH0z5OqM>taO^(_+w1c ztQ?gvVPj<_F_=(ISaB~qML59HT;#c9x(;0vkCi2#Zp`;_r@+8QOV1Ey2RWm6{*J&9 zG(Dt$zF^7qYpo9Ne}ce5re^j|rvDo*DQ&1Be#Fvo#?m4mfFrNZb1#D4f`Lf(t_Fib zwxL3lx(Zp(XVRjo_ocElY#yS$LHb6yl;9;Ycm1|5y_praEcGUZxLhS%7?b&es2skI z9l!O)b%D=cXBa@v9;64f^Q9IV$xOkl;%cG6WLQ`_a7I`woHbEX&?6NJ9Yn&z+#^#! zc8;5=jt~Unn7!cQa$=a7xSp}zuz#Lc#Q3-e7*i`Xk5tx_+^M~!DlyBOwVEq3c(?`@ zZ_3qlTN{eHOwvNTCLOHjwg0%niFYm({LEfAieI+k;U2&uTD4J;Zg#s`k?lxyJN<$mK6>j?J4eOM@T*o?&l@LFG$Gs5f4R*p*V1RkTdCfv9KUfa< z{k;#JfA3XA5NQJziGd%DchDR*Dkld&t;6i9e2t7{hQPIG_uDXN1q0T;IFCmCcua-e z`o#=uS2_en206(TuB4g-!#=rziBTs%(-b1N%(Bl}ea#xKK9zzZGCo@<*i1ZoETjeC zJ)ll{$mpX7Eldxnjb1&cB6S=7v@EDCsmIOBWc$p^W*;C0i^Hc{q(_iaWtE{0qbLjxWlqBe%Y|A z>I|4)(5mx3VtwRBrano|P))JWybOHUyOY67zRst259tx;l(hbY@%Z`v8Pz^0Sw$?= zwSd^HLyL+$l&R+TDnbV_u+h{Z>n$)PMf*YGQ}1Df@Nr{#Gr+@|gKlnv?`s1rm^$1+ zic`WeKSH?{+E}0^#T<&@P;dFf;P5zCbuCOijADb}n^{k=>mBehDD6PtCrn5ZBhh2L zjF$TbzvnwT#AzGEG_Rg>W1NS{PxmL9Mf69*?YDeB*pK!&2PQ7!u6eJEHk5e(H~cnG zZQ?X_rtws!;Tod88j=aMaylLNJbgDoyzlBv0g{2VYRXObL=pn!n8+s1s2uTwtZc

YH!Z*ZaR%>WTVy8-(^h5J^1%NZ$@&_ZQ)3AeHlhL~=X9=fKPzFbZ;~cS**=W-LF1 z5F82SZ zG8QZAet|10U*jK*GVOA(iULStsUDMjhT$g5MRIc4b8)5q_a?ma-G+@xyNDk{pR*YH zjCXynm-fV`*;}%3=+zMj**wlCo6a{}*?;`*j%fU`t+3Korws%dsCXAANKkmVby*eJ z6`2%GB{+&`g2;snG`LM9S~>#^G|nZ|JMnWLgSmJ4!kB->uAEF0sVn6km@s=#_=d)y zzld%;gJY>ypQuE z!wgqqTSPxaUPoG%FQ()1hz(VHN@5sfnE68of>9BgGsQP|9$7j zGqN{nxZx4CD6ICwmXSv6&RD<-etQmbyTHIXn!Q+0{18=!p))>To8df$nCjycnW07Q zsma_}$tY#Xc&?#OK}-N`wPm)+2|&)9=9>YOXQYfaCI*cV1=TUl5({a@1wn#V?y0Yn z(3;3-@(QF|0PA}|w4hBWQbTItc$(^snj$36kz{pOx*f`l7V8`rZK}82pPRuy zxwE=~MlCwOLRC`y%q8SMh>3BUCjxLa;v{pFSdAc7m*7!}dtH`MuMLB)QC4B^Uh2_? zApl6z_VHU}=MAA9*g4v-P=7~3?Lu#ig)cRe90>@B?>})@X*+v&yT6FvUsO=p#n8p{ zFA6xNarPy0qJDO1BPBYk4~~LP0ykPV ztoz$i+QC%Ch%t}|i^(Rb9?$(@ijUc@w=3F1AM}OgFo1b89KzF6qJO~W52U_;R_MsB zfAC29BNUXpl!w&!dT^Zq<__Hr#w6q%qS1CJ#5Wrb*)2P1%h*DmZ?br)*)~$^TExX1 zL&{>xnM*sh=@IY)i?u5@;;k6+MLjx%m(qwDF3?K3p>-4c2fe(cIpKq#Lc~;#I#Wwz zywZ!^&|9#G7PM6tpgwA@3ev@Ev_w`ZZRs#VS4}<^>tfP*(uqLL65uSi9H!Gqd59C&=LSDo{;#@Isg3caF1X+4T}sL2B+Q zK*kO0?4F7%8mx3di$B~b&*t7y|{x%2BUg4kLFXt`FK;Vi(FIJ+!H zW;mjBrfZdNT>&dDfc4m$^f@k)mum{DioeYYJ|XKQynXl-IDs~1c(`w{*ih0-y_=t$ zaMDwAz>^CC;p*Iw+Hm}%6$GN49<(rembdFvb!ZyayLoqR*KBLc^OIA*t8CXur+_e0 z3`|y|!T>7+jdny7x@JHtV0CP1jI^)9){!s#{C>BcNc5#*hioZ>OfDv)&PAM!PTjS+ zy1gRZirf>YoGpgprd?M1k<;=SShCMn406J>>iRVnw9QxsR|_j5U{Ixr;X5n$ih+-=X0fo(Oga zB=uer9jc=mYY=tV-tAe@_d-{aj`oYS%CP@V3m6Y{)mZ5}b1wV<9{~$`qR9 zEzXo|ok?1fS?zneLA@_C(BAjE_Bv7Dl2s?=_?E9zO5R^TBg8Be~fpG?$9I; zDWLH9R9##?>ISN8s2^wj3B?qJxrSSlC6YB}Yee{D3Ex8@QFLZ&zPx-?0>;Cafcb-! zlGLr)wisd=C(F#4-0@~P-C&s%C}GvBhb^tTiL4Y_dsv@O;S56@?@t<)AXpqHx9V;3 zgB!NXwp`=%h9!L9dBn6R0M<~;(g*nvI`A@&K!B`CU3^FpRWvRi@Iom>LK!hEh8VjX z_dSw5nh-f#zIUDkKMq|BL+IO}HYJjMo=#_srx8cRAbu9bvr&WxggWvxbS_Ix|B}DE zk!*;&k#1BcinaD-w#E+PR_k8I_YOYNkoxw5!g&3WKx4{_Y6T&EV>NrnN9W*@OH+niSC0nd z#x*dm=f2Zm?6qhY3}Kurxl@}d(~ z<}?Mw+>%y3T{!i3d1%ig*`oIYK|Vi@8Z~*vxY%Od-N0+xqtJ*KGrqo*9GQ14WluUn z+%c+og=f0s6Mcf%r1Be#e}&>1n!!ZxnWZ`7@F9ymfVkuFL;m6M5t%6OrnK#*lofS{ z=2;WPobvGCu{(gy8|Mn(9}NV99Feps6r*6s&bg(5aNw$eE ztbYsrm0yS`UIJ?Kv-EpZT#76g76*hVNg)L#Hr7Q@L4sqHI;+q5P&H{GBo1$PYkr@z zFeVdcS?N1klRoBt4>fMnygNrDL!3e)k3`TXoa3#F#0SFP(Xx^cc)#e2+&z9F=6{qk z%33-*f6=+W@baq){!d_;ouVthV1PREX^ykCjD|%WUMnNA2GbA#329aEihLk~0!!}k z)SIEXz(;0lemIO{|JdO{6d|-9LePs~$}6vZ>`xYCD(ODG;OuwOe3jeN;|G$~ml%r* z%{@<9qDf8Vsw581v9y+)I4&te!6ZDJMYrQ*g4_xj!~pUu#er`@_bJ34Ioez)^055M$)LfC|i*2*3E zLB<`5*H#&~R*VLYlNMCXl~=9%o0IYJ$bY+|m-0OJ-}6c@3m<~C;;S~#@j-p?DBdr<><3Y92rW-kc2C$zhqwyq09;dc5;BAR#PPpZxqo-@e_s9*O`?w5 zMnLUs(2c-zw9Pl!2c#+9lFpmTR>P;SA#Id;+fo|g{*n&gLi}7`K)(=tcK|?qR4qNT z%aEsSCL0j9DN$j8g(a+{Z-qPMG&O)H0Y9!c*d?aN0tC&GqC+`%(IFY$ll~!_%<2pX zuD`w_l)*LTG%Qq3ZSDE)#dt-xp<+n=3&lPPzo}r2u~>f8)mbcdN6*r)_AaTYq%Scv zEdwzZw&6Ls8S~RTvMEfX{t@L4PtDi{o;|LyG>rc~Um3;x)rOOGL^Bmp0$TbvPgnwE zJEmZ>ktIfiJzdW5i{OSWZuQWd13tz#czek~&*?iZkVlLkgxyiy^M~|JH(?IB-*o6% zZT8+svJzcVjcE0UEkL_5$kNmdrkOl3-`eO#TwpTnj?xB}AlV2`ks_Ua9(sJ+ok|%b z=2n2rgF}hvVRHJLA@9TK4h#pLzw?A8u31&qbr~KA9;CS7aRf$^f1BZ5fsH2W8z}FU zC}Yq76IR%%g|4aNF9BLx6!^RMhv|JYtoZW&!7uOskGSGL+}_>L$@Jg2Vzugq-NJW7 zzD$7QK7cftU1z*Fxd@}wcK$n6mje}=C|W)tm?*V<<{;?8V9hdoi2NRm#~v^#bhwlc z5J5{cSRAUztxc6NH>Nwm4yR{(T>0x9%%VeU&<&n6^vFvZ{>V3RYJ_kC9zN(M(` zp?1PHN>f!-aLgvsbIp*oTZv4yWsXM2Q=C}>t7V(iX*N8{aoWphUJ^(n3k`pncUt&` ze+sYjo)>>=I?>X}1B*ZrxYu`|WD0J&RIb~ zPA_~u)?&`}JPwc1tu=OlKlJ3f!9HXa)KMb|2%^~;)fL>ZtycHQg`j1Vd^nu^XexYkcae@su zOhxk8ws&Eid_KAm_<}65zbgGNzwshR#yv&rQ8Ae<9;S^S}Dsk zubzo?l{0koX8~q*{uA%)wqy*Vqh4>_Os7PPh-maB1|eT-4 zK>*v3q}TBk1QlOF!113XOn(Kzzb5o4Dz@?q3aEb9%X5m{xV6yT{;*rnLCoI~BO&SM zXf=CHLI>kaSsRP2B{z_MgbD;R_yLnd>^1g`l;uXBw7|)+Q_<_rO!!VaU-O+j`u%zO z1>-N8OlHDJlAqi2#z@2yM|Dsc$(nc>%ZpuR&>}r(i^+qO+sKfg(Ggj9vL%hB6 zJ$8an-DbmKBK6u6oG7&-c0&QD#?JuDYKvL5pWXG{ztpq3BWF)e|7aF-(91xvKt047 zvR{G@KVKz$0qPNXK*gt*%qL-boz-*E;7LJXSyj3f$7;%5wj)2p8gvX}9o_u}A*Q|7 z)hjs?k`8EOxv1zahjg2PQDz5pYF3*Cr{%iUW3J+JU3P+l?n%CwV;`noa#3l@vd#6N zc#KD2J;5(Wd1BP)`!IM;L|(d9m*L8QP|M7W#S7SUF3O$GFnWvSZOwC_Aq~5!=1X+s z6;_M++j0F|x;HU6kufX-Ciy|du;T%2@hASD9(Z)OSVMsJg+=7SNTAjV<8MYN-zX5U zVp~|N&{|#Z)c6p?BEBBexg4Q((kcFwE`_U>ZQotiVrS-BAHKQLr87lpmwMCF_Co1M z`tQI{{7xotiN%Q~q{=Mj5*$!{aE4vi6aE$cyHJC@VvmemE4l_v1`b{)H4v7=l5+lm^ ztGs>1gnN(Vl+%VuwB+|4{bvdhCBRxGj3ady^ zLxL@AIA>h@eP|H41@b}u4R`s4yf9a2K!wGcGkzUe?!21Dk)%N6l+#MP&}B0%1Ar*~ zE^88}(mff~iKMPaF+UEp5xn(gavK(^9pvsUQT8V;v!iJt|7@&w+_va`(s_57#t?i6 zh$p!4?BzS9fZm+ui`276|I307lA-rKW$-y^lK#=>N|<-#?WPPNs86Iugsa&n{x%*2 zzL_%$#TmshCw&Yo$Ol?^|hy{=LYEUb|bMMY`n@#(~oegs-nF){0ppwee|b{ca)OXzS~01a%cg&^ zp;}mI0ir3zapNB)5%nF>Sd~gR1dBI!tDL z&m24z9sE%CEv*SZh1PT6+O`%|SG>x74(!d!2xNOt#C5@I6MnY%ij6rK3Y+%d7tr3&<^4XU-Npx{^`_e z9$-|@$t`}A`UqS&T?cd@-+-#V7n7tiZU!)tD8cFo4Sz=u65?f#7Yj}MDFu#RH_GUQ z{_-pKVEMAQ7ljrJ5Wxg4*0;h~vPUI+Ce(?={CTI&(RyX&GVY4XHs>Asxcp%B+Y9rK z5L$q94t+r3=M*~seA3BO$<0%^iaEb2K=c7((dIW$ggxdvnC$_gq~UWy?wljgA0Dwd`ZsyqOC>)UCn-qU5@~!f znAWKSZeKRaq#L$3W21fDCMXS;$X(C*YgL7zi8E|grQg%Jq8>YTqC#2~ys%Wnxu&;ZG<`uZ1L<53jf2yxYR3f0>a;%=$SYI@zUE*g7f)a{QH^<3F?%({Gg)yx^zsdJ3^J2 z#(!C3qmwx77*3#3asBA(jsL`86|OLB)j?`0hQIh>v;c2A@|$Yg>*f+iMatg8w#SmM z<;Y?!$L--h9vH+DL|Wr3lnfggMk*kyGH^8P48or4m%K^H-v~`cBteWvnN9port02u zF;120HE2WUDi@8?&Oha6$sB20(XPd3LhaT~dRR2_+)INDTPUQ9(-370t6a!rLKHkIA`#d-#WUcqK%pMcTs6iS2nD?hln+F-cQPUtTz2bZ zq+K`wtc1;ex_iz9?S4)>Fkb~bj0^VV?|`qe7W02H)BiibE9=_N8=(5hQK7;(`v7E5Mi3o? z>J_)L`z(m(27_&+89P?DU|6f9J*~Ih#6FWawk`HU1bPWfdF?02aY!YSo_!v$`&W znzH~kY)ll^F07=UNo|h;ZG2aJ<5W~o7?*${(XZ9zP0tTCg5h-dNPIM=*x@KO>a|Bk zO13Cbnbn7+_Kj=EEMJh4{DW<))H!3)vcn?_%WgRy=FpIkVW>NuV`knP`VjT78dqzT z>~ay~f!F?`key$EWbp$+w$8gR1RHR}>wA8|l9rl7jsT+>sQLqs{aITUW{US&p{Y)O zRojdm|7yoA_U+`FkQkS?$4$uf&S52kOuUaJT9lP@LEqjKDM)iqp9aKNlkpMyJ76eb zAa%9G{YUTXa4c|UE>?CCv(x1X3ebjXuL&9Dun1WTlw@Wltn3zTareM)uOKs$5>0tR zDA~&tM~J~-YXA<)&H(ud)JyFm+d<97d8WBr+H?6Jn&^Ib0<{6ov- ze@q`#Y%KpD?(k{if5-M(fO3PpK{Wjqh)7h+ojH ztb=h&vmy0tn$eA8_368TlF^DKg>BeFtU%3|k~3lZAp(C$&Qjo9lR<#rK{nVn$)r*y z#58_+t=UJm7tp|@#7}6M*o;vn7wM?8Srtc z3ZFlKRDYc^HqI!O9Z*OZZ8yo-3ie9i8C%KDYCfE?`rjrf(b&xBXub!54yaZY2hFi2w2asEOiO8;Hru4~KsqQZMrs+OhO8WMX zFN0=EvME`WfQ85bmsnPFp|RU;GP^&Ik#HV(iR1B}8apb9W9)Nv#LwpED~%w67o;r! zVzm@zGjsl)loBy6p>F(G+#*b|7BzZbV#E0Pi`02uAC}D%6d12TzOD19-9bhZZT*GS zqY|zxCTWn+8*JlL3QH&eLZ}incJzgX>>i1dhff}DJ=qL{d?yv@k33UhC!}#hC#31H zOTNv5e*ozksj`4q5H+75O70w4PoA3B5Ea*iGSqA=v)}LifPOuD$ss*^W}=9kq4qqd z6dqHmy_IGzq?j;UzFJ*gI5)6qLqdUL;G&E*;lnAS+ZV1nO%OdoXqw(I+*2-nuWjwM-<|XD541^5&!u2 z1XflFJp(`^D|ZUECbaoqT5$#MJ=c23KYpBjGknPZ7boYRxpuaO`!D6C_Al?T$<47T zFd@QT%860pwLnUwer$BspTO9l1H`fknMR|GC?@1Wn`HscOe4mf{KbVio zahne0&hJd0UL#{Xyz=&h@oc>E4r*T|PHuNtK6D279q!2amh%r#@HjaN_LT4j>{&2I z?07K#*aaZ?lNT6<8o85cjZoT~?=J&Xd35I%JJom{P=jj?HQ5yfvIR8bd~#7P^m%B-szS{v<)7i?#at=WA+}?r zwMlc-iZv$GT};AP4k2nL70=Q-(+L_CYUN{V?dnvG-Av+%)JxfwF4-r^Z$BTwbT!Jh zG0YXK4e8t`3~){5Qf6U(Ha0WKCKl^zlqhqHj~F}DoPV#yHqLu+ZWlv2zH29J6}4amZ3+-WZkR7(m{qEG%%57G!Yf&!Gu~FDeSYmNEkhi5nw@#6=Bt& zOKT!UWVY-FFyq1u2c~BJ4F`39K7Vw!1U;aKZw)2U8hAb&7ho|FyEyP~D<31{_L>RrCU>eEk-0)TBt5sS5?;NwAdRzRj5qRSD?J6 ze9ueq%TA*pgwYflmo`=FnGj2r_u2!HkhE5ZbR_Xf=F2QW@QTLD5n4h(?xrbOwNp5` zXMEtm`m52{0^27@=9VLt&GI;nR9S)p(4e+bAO=e4E;qprIhhclMO&7^ThphY9HEko z#WfDFKKCcf%Bi^umN({q(avHrnTyPH{o=sXBOIltHE?Q65y_At<9DsN*xWP|Q=<|R z{JfV?B5dM9gsXTN%%j;xCp{UuHuYF;5=k|>Q=;q zU<3AEYawUG;=%!Igjp!FIAtJvoo!*J^+!oT%VI4{P=XlbYZl;Dc467Nr*3j zJtyn|g{onj!_vl)yv)Xv#}(r)@25OHW#|eN&q7_S4i2xPA<*uY9vU_R7f};uqRgVb zM%<_N3ys%M;#TU_tQa#6I1<+7Bc+f%mqHQ}A@(y^+Up5Q*W~bvS9(21FGQRCosvIX zhmsjD^OyOpae*TKs=O?(_YFjSkO`=CJIb*yJ)Pts1egl@dX6-YI1qb?AqGtIOir&u zyn>qxbJhhJi9SjK+$knTBy-A)$@EfzOj~@>s$M$|cT5V!#+|X`aLR_gGYmNuLMVH4 z(K_Tn;i+fR28M~qv4XWqRg~+18Xb?!sQ=Dy)oRa)Jkl{?pa?66h$YxD)C{F%EfZt| z^qWFB2S_M=Ryrj$a?D<|>-Qa5Y6RzJ$6Yp`FOy6p2lZSjk%$9guVsv$OOT*6V$%TH zMO}a=JR(1*u`MN8jTn|OD!84_h${A)_eFRoH7WTCCue9X73nbD282V`VzTH$ckVaC zalu%ek#pHxAx=0migDNXwcfbK3TwB7@T7wx2 zGV7rS+2g9eIT9>uWfao+lW2Qi9L^EBu#IZSYl0Q~A^KYbQKwNU(YO4Xa1XH_>ml1v z#qS;P!3Lt%2|U^=++T`A!;V-!I%upi?<#h~h!X`p7eP!{+2{7DM0$yxi9gBfm^W?M zD1c)%I7N>CG6250NW54T%HoCo^ud#`;flZg_4ciWuj4a884oWUYV(#VW`zO1T~m(_ zkayymAJI)NU9_0b6tX)GU+pQ3K9x=pZ-&{?07oeb1R7T4RjYYbfG^>3Y>=?dryJq& zw9VpqkvgVB?&aK}4@m78NQhTqZeF=zUtBkJoz8;6LO<4>wP7{UPEs1tP69;v919I5 zzCqXUhfi~FoK5niVU~hQqAksPsD@_|nwH4avOw67#fb@Z5_OS=$eP%*TrPU%HG<-A z`9)Y3*SAdfiqNTJ2eKj8B;ntdqa@U46)B+odlH)jW;U{A*0sg@z>-?;nN}I=z3nEE@Bf3kh1B zdqT{TWJvb#AT&01hNsBz8v(OwBJSu#9}A6Y!lv|`J#Z3uVK1G`0$J&OH{R?3YVfk% z9P3HGpo<1uy~VRCAe&|c4L!SR{~^0*TbVtqej3ARx(Okl5c>m~|H9ZwKVHc_tCe$hsqA`l&h7qPP5xBgtwu!; zzQyUD<6J!M5fsV-9P?C9P49qnXR+iXt#G_AS2N<6!HZ(eS`|-ndb|y!(0Y({2 z4aF~GO8bHM7s+wnhPz>sa!Z%|!qWk*DGr)azB}j6bLe#FQXV4aO>Eo7{v`0x=%5SY zy&{kY+VLXni6pPJYG_Sa*9hLy-s$79$zAhkF)r?9&?UaNGmY9F$uf>iJ~u@Q;sydU zQaN7B>4B*V;rtl^^pa3nFh$q*c&sx^Um}I)Z)R&oLEoWi3;Yv6za?;7m?fZe>#_mS z-EGInS^#UHdOzCaMRSLh7Mr0}&)WCuw$4&K^lx{;O+?Q1p5PD8znQ~srGrygJ?b~Q5hIPt?Wf2)N?&Dae4%GRcRKL(a-2koctrcvxSslXn-k9cYS|<-KJ#+$Wo>}yKKh*3Q zHsK(4-Jv!9R3*FKmN$Z#^aZcACGrlGjOe^#Z&DfPyS-1bT9OIX~-I-5lN6Y>M}dvivbs2BcbPcaNH%25-xMkT$>*soDJ) z27;};8oCYHSLF0VawZFn8^H;hIN=J457@eoI6s2P87QN6O`q8coa;PN$mRZ>2Vv+! zQj1}Tvp8?>yyd_U>dnhx%q~k*JR`HO=43mB?~xKAW9Z}Vh2b0<(T89%eZ z57kGs@{NUHM>|!+QtqI@vE8hp`IIGc`A9Y{p?c;@a!zJFmdaCJ;JmzOJ8)B1x{yZp zi!U{Wh-h+u6vj`2F+(F6gTv*cRX7MR z9@?>is`MSS1L#?PaW6BWEd#EX4+O1x6WdU~LZaQ^Quow~ybz*aAu{ZMrQ;yQ8g)-qh>x z^}@eFu1u7+3C0|hRMD1{MEn(JOmJ|wYHqGyn*xt-Y~J3j@nY56i)sgNjS4n@Q&p@@^>HQjzNaw#C9=TbwzDtiMr2a^}bX< zZE%HU^|CnS`WYVcs}D)+fP#bW0+Q#l#JC+!`OlhffKUCN8M-*CqS;VQX`If78$as0 z=$@^NFcDpTh~45heE63=x5nmP@4hBaFn(rmTY2Yj{S&k;{4W!0Nu9O5pK30}oxM7{ z>l4cKb~9D?N#u_AleD<~8XD@23sY^rt&fN%Q0L=Ti2bV#px`RhM$}h*Yg-iC4A+rI zV~@yY7!1}-@onsZ)@0tUM23cN-rXrZYWF#!V-&>vds8rP+w0t{?~Q zT^LN*lW==+_ifPb+-yMh9JhfcYiXo_zWa`ObRP9_En3P))Qyu0qPJ3*hiFSu>Vt-j z<*HWbiP2#BK@nt<g|pe3 zfBKS@i;ISkorx@cOIx9}p^d8Gis%$)))%ByVYU^KG#eE+j1p;^(Y1ndHnV&YuQZm~ zj;f+mf>0ru!N`)_p@Ls<& z`t+JDx7}R568Q|8`4A}G@t8Wc?SOXunyW5C-AWoB@P>r}uwFY*=?=!K@J(!t@#xOuPXhFS@FTf6-7|%k;nw2%Z+iHl219Ho1!bv(Ee0|ao!Rs%Jl0@3suGrOsb_@VM;(xzrf^Cbd;CK3b%a|ih-fG)`Rd00O74=sQYW~Ve z#fl!*(fo~SIQ5-Sl?1@o7-E*|SK|hoVEKzxeg!$KmQLSTN=5N`rYeh$AH&x}JMR+5dq|~FUy&Oj%QIy;HNr;V*7cQC+ka>LAwdU)?ubI@W z={eg%A&7D**SIj$cu=CN%vN^(_JeIHMUyejCrO%C3MhOcVL~Niu;8WYoN}YVhb+=- zR}M3p|H0`E2Id99y#03r`8$s0t*iD>`^7EPm1~guC)L~uW#O~>I85Q3Nj8(sG<@T| zL^e~XQt9O0AXQ^zkMdgzk5bdYttP~nf-<831zulL>>ghTFii$lg3^80t8Gb*x1w5| zN{kZuv`^8Fj=t(T*46M=S$6xY@0~AvWaGOYOBTl0?}KTkplmGn-*P(X=o-v^48OY} zi11-+Y}y)fdy_tI;*W(>#qzvgQZ52t!nrGsJEy!c86TKIN(n|!&ucCduG$XaIapI z{(Z9gZANsI={A=5Aorgq2H25Dd}H5@-5=j=s{f`%^>6b5qkm_2|3g>r-^amf=B_xV zXg*>aqxXZ6=VUI4$})ypDMy$IKkgJ;V>077T9o#OhpFhKtHP_4mnjS5QCgGe<;~Xe zt<2ZhL7?JL6Mi|U_w?;?@4OD@=4EB2op_s)N-ehm#7`zSU#7itU$#%^ncqjc`9HCG zfj;O1T+*oTkzRi-6NN`oS3w3$7ZB37L>PcN$C$L^qqHfiYO4_>0_qCw0r@FEMj=>}}%q_`d#pUT;c?=gI zqTGpiY4Z;Q(B~#hXIVBFbi#dO=cOdmOqD0|An?7nMdrm2^C>yw*dQ=#lf8)@DvXK; z$MXp}QZgnE!&L73x0LZX_bCdD4lRY$$^?9dt1RwCng{lIpbb%Ej%yOh{@76yEyb}K zXZy%^656Sk3BLKbalcc>Dt5iDzo^tj2!wnDL(X;urJfpkWrab!frFSC6Q7m zuoqN!(t=L&+Ov&~9mz(yEB`MK%RPXS>26Ww5(F;aZ zR@tPAw~=q2ioOiynxgBqE&3-R-@6yCo0*mE;#I^c!=g~HyyjGA6}|<(0EseKDTM4w z94YnCO^VYIUY@}x8kr;;El-cFHVO<$6;-UdmUB|J8R*Wf$a37gVgYT|w5^KkYe=(i zMkA$%7;^a*$V+}e%S~&*^^O;AX9NLt@cIPc*v!lKZ)(zahAsUj%PJot19ErFU=Uk( z9Hw;Lb`V+BzVpMu;TGB9}y~ff)^mbEmF?g{{7_0SR zPgp*n)l{?>7-Ji;eWG{ln$)Bro+UJAQo6W2-23d@SI=HiFV3hR2OUcAq_9q~ye)o@ zq8WZvhg`H(?1AUZ-NM%_Cuj}eb{4wOCnqs^E1G9U4HKjqaw@4dsXWP#$wx^}XPZ0F zywsJ0aJHA>AHc^q#nhQjD3!KDFT6FaDioJ#HsZU7Wo?8WH19TJ%OMDz$XH5J4Cjdt z@crE;#JNG`&1H8ekB(R4?QiiZ55kztsx}pQti}gG0&8`dP=d(8aCLOExd*Sw^WL`Q zHvZ(u`5A58h?+G&GVsA;pQNNPFI)U@O`#~RjaG(6Y<=gKT2?1 z*pCUGU)f??VlyP64P@uT`qh?L03ZQyLOBn?EKwH+IG{XvTh5|NldaSV_n~DK&F1aa znq~C_lCQHMfW6xib%a2m!h&%J)aXb{%-0!HCcW|kzaoSwPMhJ6$KL|F~Sx(tctbwfkgV;#KZlEmJN5&l5XF9eD;Kqb<| z>os)CqC^qF8$be|v;)LY{Gh@c0?a??k7M7&9CH+-B)t&T$xeSzCs30sf8O-+I#rq} z&kZj5&i>UyK9lDjI<*TLZ3USVwwpiE5x8<|{Db z3`HX3+Tt>1hg?+uY{^wC$|Tb7ud@3*Ub?=2xgztgv6OOz0G z-4VRyIChHfegUak^-)-P;VZY@FT64#xyo=+jG<48n2%wcx`ze6yd51(!NclmN=$*kY=#uu#>=yAU-u4I9Bt0n_6ta?&9jN+tM_5_3RH);I zxTN4n$EhvKH%TmOh5mq|?Cx$m>$Ed?H7hUEiRW^lnW+}ZoN#;}aAuy_n189qe1Juk z6;QeZ!gdMAEx4Na;{O*j$3F3e?FLAYuJ2iuMbWf8Ub6(nDo?zI5VNhN@ib6Yw_4P)GY^0M7TJwat z2S*2AcP}e0tibZ@k&htTD&yxT9QRG0CEq$;obfgV^&6YVX9B9|VJf`1aS_#Xk>DFo zwhk?~)>XlP5(u~UW0hP7dWZuCuN4QM24Td&j^7~)WQ6YeCg)njG*ri}tTcG-NxX}p zNB>kcxd5ipW@tN3=6r@Jgm#rgrK*dXA!gxy6fAvP7$)8)Vc~PPQ|`( zPy|bG1sUz958-!zW^j(8ILV%QC@x`~PDFczboZqWjvSU<9O3!TQ&xYi%?Y0AiVBLV z%R?#1L#G&xw*RZPsrwF?)B5+MSM(b$L;GLnRsSU!_$N;6pD97~H}`c>0F`&E_FCNE z_)Q*EA1%mOp`z>+h&aqlLKUD9*w?D>stDeBRdR*AS9)u;ABm7w1}eE|>YH>YtMyBR z^e%rPeZzBx_hj?zhJVNRM_PX(O9N#^ngmIJ0W@A)PRUV7#2D!#3vyd}ADuLry;jdn zSsTsHfQ@6`lH z^GWQf?ANJS>bBO-_obBL$Apvakhr1e5}l3axEgcNWRN$4S6ByH+viK#CnC1|6Xqj& z*_i7cullAJKy9GBAkIxUIzsmN=M|(4*WfBhePPHp?55xfF}yjeBld7+A7cQPX8PE-|Pe_xqboE;2AJb5ifrEfr86k&F0+y!r`-urW}OXSkfz2;E``UTrGSt^B)7&#RSLTQitk=mmPKUKP`uGQ4)vp_^$^U`2Jjq zeul!ptEpa%aJo0S(504oXPGdWM7dAA9=o9s4-{>z*pP zJ31L#|L?YR;^%+>YRJrLrFC=5vc;0{hcxDKF z!ntmgO>rVDaGmRpMI7-+mv(j~;s_LARvcpkXj|{GHu1c<1 zKI)#7RE~Dizu1lG>p-PcY2jX#)!oJlBA$LHnTUWX=lu``E)vhf9h4tYL-juZ`e|Kb z=F?C;Ou)h^cxB;M-8@$ZSH0jkVD>x-XS$ePV1vlU8&CG))4NgU(=XFH=Jb1IB7dBysS+94}Y>sjS(&YcJwhn zifzA|g$D5rW89vkJSv()I+Th4R&C$g-!CB30xkh%aw4po3$@DK2fW>}enE2YPt&{C~j}`>RYICK{ zYAPfZ&%`R}u6MYo<>d`^O#Q(dM{3>T^%J{Vu;lr#Utg4x9!Z9J%iXs(j+dn&SS1_2 zzxGtMnu^`d%K4Xq4Ms-ErG3_7n?c(3T!?rvyW=G<7_XKDv*ox`zN*^BVwUoqh{D7o zdEiq;Zp6}k_mCIAVTUcMdH|fo%L#qkN19X$%b1#Oko|u4!M*oRqdBa3z98{H#g=d%5X&D#NXhLh`nUjxi8@3oo(AgeItdJ zIrt9ieHI1GiwHiU4Cba-*nK@eHI4uj^LVmVIntU@Gwf^t6i3{;SfLMCs#L;s;P4s5oqd^}8Uil!NssP>?!K z07nAH>819U=^4H6l-Dhy`^Q6DV^}B9^aR0B%4AH=D&+dowt9N}zCK+xHnXb-tsKaV6kjf;Wdp#uIZ_QsI4ralE>MWP@%_5eN=MApv92( z09SSB#%eE|2atm9P~X2W2F-zJD+#{q9@1}L2fF|Lzu@1CAJq*d6gA8*Jjb;<+Asih zctE|7hdr5&b-hRhVe}PN z$0G{~;pz1yhkbwuLkfbvnX=<7?b(1PhxAmefKn$VS6Sv)t-UypwhEs3?*E=(pc%Dlul1V~OdWvdf z{WBX?lhfO_g$$X~hm^Bhl@U0t<|beYgT)2L_C(z@B^-63c9Ak2*Aa)iOMylfl|qyNQdO#yoJ?m2FOkhZ1ou@G%+^m z#!#(gTv8nx^34(HddDp|dcFl@&eh+&FFJc@^FL3fV2?u&9Wt|Yp3&MS)e+ez0g~Ys zY7d0n^)+ z0@K^GJTLN?XAV(0F6e>o>HCGJU5(8WsSFErs0FsO=O1u$=T~xx7HYK{7C>-IGB8U+ z&G^Vy>uY}Bq7HX-X`U^nNh+11GjG-)N1l_tG<^4Tu4+4X9KO9IrdH+eXGk|G6Tc(U zU~g7BoO!{elBk>;uN-`rGQP-7qIf9lQhj-=_~0Qyszu>s$s0FrJatSylv!ol&{29~ z7S4fv&-UBOF&cR@xpuW*{x9$R;c_ALt?{+dI&HoBKG-!EY{yE=>aWhlmNhHlCXc(B zuA-zI*?Z9ohO$i8s*SEIHzVvyEF$65b5m=H*fQ)hi*rX8 zKlPqjD*Ix1tPzfR_Z3bO^n32iQ#vhjWDwj6g@4S?_2GyjiGdZZRs3MLM zTfl0_Dsn=CvL`zRey?yi)&4TpF&skAi|)+`N-wrB_%I_Osi~)9`X+`Z^03whrnP7f z?T`*4Id`J@1x#T~L(h5^5z%Cok~U|&g&GpCF%E4sB#i3xAe>6>24%Kuu=)=HRS;Pu2wghgTFa zHqm#sa{7-~{w_039gH0vrOm&KPMiPmuPRpAQTm5fkPTZVT&9eKuu%Riu%-oMQl2X6 z{Bnx`3ro^Z$}rVzvUZsk9T)pX|4%sY+j0i)If_z-9;a^vr1YN>=D(I7PX){_JTJ&T zPS6~9iDT{TFPn}%H=QS!Tc$I9FPgI<0R7?Mu`{FTP~rRq(0ITmP1yrJdy|m;nWmDelF-V^y7*UEVvbxNv0sHR?Q=PVYRuZinR(;RjVAG zm&qlSYvaiIbVEqBwyDaJ8LVmiCi{6ESF4pO?U&7pk&CASm6vuB;n-RauPFzdr!C%1 z8pjdSUts7EbA4Kg(01zK!ZU<-|d zU&jWswHnSLIg&mTR;!=-=~z(#!UsXt%NJR|^teM8kG@8Qg_0^6Jqfn&(eENtP8D7K zvnll3Y%7yh1Ai~0+l6dAG|lEGe~Oa+3hO>K2}{ulO?Vf*R{o2feaRBolc;SJg)HXHn4qtzomq^EM zb)JygZ=_4@I_T=Xu$_;!Q`pv6l)4E%bV%37)RAba{sa4T*cs%C!zK?T8(cPTqE`bJ zrBWY`04q&+On`qH^KrAQT7SD2j@C>aH7E8=9U*VZPN-(x>2a++w7R$!sHH+wlze2X)<<=zC_JJvTdY7h&Jum?s?VRV)JU`T;vjdi7N-V)_QCBzI zcWqZT{RI4(lYU~W0N}tdOY@dYO8Rx5d7DF1Ba5*U7l$_Er$cO)R4dV zE#ss{Dl`s#!*MdLfGP>?q2@GSNboVP!9ZcHBZhQZ>TJ85(=-_i4jdX5A-|^UT}~W{CO^Lt4r;<1ps@s|K7A z90@6x1583&fobrg9-@p&`Gh+*&61N!$v2He2fi9pk9W2?6|)ng7Y~pJT3=g~DjTcYWjY9gtZ5hk*1Qf!y2$ot@0St$@r8|9^GMWEE>iB~etL zXYxn#Rvc`DV&y93@U$Z91md1qVtGY*M(=uCc}@STDOry@58JNx`bUH}EIb(n6I}i? zSYJOZ2>B6&Payu+@V!gxb;)_zh-{~qtgVwQ-V;vK7e0^Ag_$3+g+{xSVudVOY_p-R z$sXhpFSk7je2lk5)7Y2;Z847E1<;5?;z(I)55YFtgF!J;NT|eVi}q^*2sM}zyM{+s zD0phl+J>k1E7cZEGmP?1-3~RE;R$q(I5}m?MX8xi?6@0f#rD8Cjkpv1GmL5HVbTnM zAQ&4-rbkpdaoLp~?ZoW>^+t0t1t%GO2B;ZD4?{qeP+qsjOm{1%!oy1OfmX?_POQJ4 zGwvChl|uE;{zGoO?9B_m{c8p(-;_yq?b^jA({}iQG35?7H7`1cm`BGyfuq7z1s~T| zm88HpS{z54T{jxC=>kZ=Z#8G@uya3tt0$xST5V$-V<;6MA66VFg}`LLU8L=q3DmkU z)P^X8pg`ndMY*>gr{6~ur^Q@Z8LNQf*6wkP03K<|M*+cDc#XKZ`Z0$1FkI-IDRw#| za52W4MyHlDABs~AQu7Duebjgc}02W;1jgBx&I@TMDXU`LJutQ?@r%1z`W zlB8G-U$q37G1ob>Er8j0$q@OU3IwG#8HsvJM#)j=Y%~#zY`jaG%5;!(kY3*a^t>(qf6>I zpAJpF%;FQ?BhDSsVG27tQEG*CmWhl4)Ngp%}D?U0!nb1=)1M==^B)^$8Li$boCY$S4U;G^A!?24nSYHra{< zSNapX#G+0BTac|xh`w&}K!);$sA3ay%^a2f?+^*9Ev8ONilfwYUaDTMvhqz2Ue2<81uuB71 zAl|VEOy%GQ7zxAJ&;V^h6HOrAzF=q!s4x)Mdlmp{WWI=gZRk(;4)saI0cpWJw$2TJcyc2hWG=|v^1CAkKYp;s_QmU?A;Yj!VQ1m-ugzkaJA(wQ_ zah00eSuJg<5Nd#OWWE?|GrmWr+{-PpE_Dbqs&2`BI=<%ggbwK^8VcGiwC-6x`x|ZY z1&{Vj*XIF2$-2Lx?KC3UNRT z&=j7p1B(akO5G)SjxXOjEzujDS{s?%o*k{Ntu4*X z;2D|UsC@9Wwk5%)wzTrR`qJX!c1zDZXG>-Q<3Z)7@=8Y?HAlj_ZgbvOJ4hPlcH#Iw z!M-f`OSHF~R5U`p(3*JY=kgBZ{Gk;0;bqEu%A;P6uvlZ0;BAry`VUoN(*M9NJ z%CU2_w<0(mSOqG;LS4@`p(3*Z7jC|Khm5-i>FcYr87};_J9)XKlE}(|HSfnA(I3)I zfxNYZhs#E6k5W(z9TI2)qGY&++K@Z?bd;H%B@^!>e2Wi@gLk)wC)T93gTxdRPU7uh z)`$-m(G2I5AuK52aj!fMJR|d^H?0X~+4xSpw zqNRtq5r8hic*{eAwUT<=gI5uXLg)o5mg4XnO^T+Rd+{l)<$Aqp{+RxhNYuX^45W0k z5$t%+7R;dX$`s6CYQYcims>5bNt+k&l_t%C9D-6sYVm%Y8SRC#kgRh*%2kqMg2ewb zp_X*$NFU%#$PuQ@ULP>h9Xw`cJ>J-ma8lU`n*9PcWFpE%x0^}(DvOVe2jz@ z0^2QOi0~t!ov?jI{#bw~`Aj5ymQW@eruRg`ZNJ5IT5_5AHbQ?|C>_7rwREf2e2x&L zlV8xdOkp_*+wdaqE?6bmdrFfaGepcj=0AI<+c=Tg^WB9BhFx?SvwoVdTEm&zPy@Vs zPs2mVPiw1n_h?Xi6!+w)ypsFXXuM>gIY(J+1N6r!sJ{+r1%BzRF20!D;bN>L^?O8n z(5|x2p^Q6X`!pm3!MMFET5`nJXn>tK`fFAj5Eo&t6;F>TU_4G93YGyzvF2_fB& zfE8(dq?R@@&Wh8~%G~rDt1+e)96O5)by_%;G~Zv`TpmZ)vY@BkAan*zEy(s`*{-@U z;$WPjoNx~m?`6Z;^O=K3SBL3LrIxfU{&g)edERkPQZK!mVYU-zHuV0ENDq^e<-?^U zGyRcrPDZZw*wxK(1SPUR$0t0Wc^*u_gb*>qEOP102FX|`^U%n*7z=wM@pOmYa6Z=-)T%!{tAFELY2`dTl3$&w! z7sgKXCTU(h3+8)H#Qov19%85Xo+oQh?C-q0zaM_X2twSCz|j_u!te3J2zLV#Ut_q7 zl+5LGx#{I`(9FzE$0==km|?%m?g~HB#BSz2vHynf1x14mEX^~pej*dhzD|6gMgOJ_ z8F_<>&OIz;`NSqrel?HI-K(|ypxwz}NtX!CF3&T(CkuYOnKS&%lUSU44KsgS`L>!w zl{MoT4`t=+p8>@88)Ea%*hOIkxt#b4RfrwRMr91UF_Ic~kV;|+dRW0a8Vl725+gsvtHr5 z>?3fai&9NmU|3;-nAu8OB|<(-2Kfub4MX&1i}dDd=R~Dk=U-Vr=@&lfEIYU~xtHHO z4TKt=wze`qm=69lD)sOOkZ;$9=0B#*g@X6xPM-%zG*rCXkN%eRDEUp$gAaEd29t&T zRTAg##Sk+TAYaa(LyTD__zL3?Z+45^+1o}(&f<~lQ*-z7`Um^>v@PKqOunTE#OyKFY^q&L^fqZgplhXQ>P3?BMaq6%rO5hfsiln7TppJ z>nG9|2MmL|lShn4-yz0qH>+o;Fe`V!-e*R0M|q~31B=EC$(bQZTW^!PrHCPE4i|>e zyAFK!@P}u>@hqwf%<#uv*jen5xEL|v!VQEK!F`SIz_H8emZfn#Hg}}@SuqPv+gJ@- zf3a`DT_Q#)DnHv+XVXX`H}At zmQwW2K`t@(k%ULJrBe6ln9|W8+3B*pJ#-^9P?21%mOk(W1{t#h?|j0ZrRi_dwGh#*eBd?fy(UBXWqAt5I@L3=@QdaiK`B_NQ$ zLXzm{0#6zh2^M zfu>HFK^d`&v|x&xxa&M|pr))A4)gFw<_X@eN`B1X%C^a{$39fq`(mOG!~22h)DYut z(?MONP1>xp4@dIN^rxtMp&a^yeGc8gmcajyuXhgaB;3}vFCQFa!pTDht9ld9`&ql`2&(dwNl5FZqedD^BP zf5K1`(_&i7x-&rD=^zkFD87idQrk(Y?E;-j^DMCht`A8Qa5J-46@G_*Y3J+&l{$}*QCATEc9zuzaQGHR8B;y*>eWuv)E##?Ba3w= zZ|v(l{EB`XzD#|ncVm#Wy?#Nzm3bS1!FJ70e{DGe$EgNDg7<_ic^mJSh&Xc|aTwCrTv;XkW~UlS&G%KyLklCn}F^i(YP(f z{cqH%5q9ND_S;l$HRP$Q@`D=F*_1$CXIA5X@|V&Vir$NQ$vCx!b&LGCR<-2y)m%HI zxeeyQIjiWcf4uD9+FP+EJ`&$oJ%$R(#w~GjqP|aTQj#d(;l#rq$vcM&Y4ZQ_i{Kpx z?k2BtoKb?+1-EVmG^ne-W%8+y?i#J5N5g8f^qpH5(ZZp7$u+?I9GB+&MREX?TmVV$ zA}Ps=^CkD^sD9N;tNtN!a>@D^&940cTETu*DUZlJO*z7BBy`Rl;$-D@8$6PFq@tz0 z=_2JMmq-JRSvx`;!XM|kO!|DENI-5ke8WR*Zj#vy#Nf1;mW-{6>_sCO8?sVWOKDM| zR(iaZrBrzlRatUzp_Y|2nOXnY2G%WLGXCo9*)th_RnXvXV=q;WNAimI98!A54|$&OCCG%$4m{%E&o?S|Qx<4K~YGmM1CS!vZAzLN%d znbZsw6ql=XkiwSbNofNeA42q8#LH6Rk(u@z172O#6K>Sb{#`t#GUgpd{2;D(9@I_9 zwsY(6Go7RmOThs2rM3|Z#Vbs}CHPLgBK6gE8;XkJQDx~p5wJ?XkE(0<^hwnt6;$~R zXCAzMfK@`myzdkkpv*ZbarVwCi&{-O#rswrb-#x4zRkxfVCq;mJLic|*C92T?0CYv z)FCqY$xA(QZmggPocZqQj0Rc?=Afna`@fpSn)&nSqtI}?;cLphqEF3F9^OZfW9@HDunc^2{_H)1D9(O}4e zJMi_4(&$CD{Jf5&u|7#Iq*F~)l!8pAzNrX^<&wfEu~}Ipslzx=g^ff2?B9SnV=!$ zv&K0`hMN6BVIusHNX-lr`#K?OG1S*S4rCQaI3ea(!gCl7YjxJ3YQ)7-b&N*D8k><*x|47s3; z4f~WTWuk|Qd*d*DICV}Vb0YSzFZp5|%s4}@jvtTfm&`|(jNpajge zD}@CMaUBs+b?Yu6&c#18=TxzMCLE76#Dy=DLiq_a_knQX4Uxk$&@3ORoBFK_&a>`QKaWu^)Hzrqz{5)?h3B_`4AOn{fG9k zEwnjQb>8XRq!k?rmCd6E**1cY#b9yczN4mD%GLCeRk}{TmR1*!dTNzY;(f!B0yVuk zSjRyf;9i@2>bdGSZJ=FNrnxOExb075;gB z*7&YR|4ZraFO#45-4h%8z8U}jdt?83AmU3)Ln#m3GT!@hYdzqqDrkeHW zU#R`Z8RHq996HR=mC}SRGtsz07;-C-!n*ALpwwBe~loM)YqMH)Um$sH0RbTTzxFd)h1=-w5Yl3k|3nQ zZG>=_yZ7Lsn=b8_MZI+LSHLGYSSCc?ht~7cv#39>Moz6AS}5 zus?xge0PGdFd2FpXgIscWOyG}oxATgd$yl0Ugf_&J_vwt`)XWx!p*gE_cWU(tUTnz zQS}!bMxJyi3KWh^W9m zxLcy``V@EfJzYjK@$e7Yk=q!kL8cd3E-zpc*wwvGJ62O!V;N zFG7Y?sJ+^a%H1;rdDZRu2JmGn6<&ERKes=Pwx)GG-nt73&M78+>SOy!^#=gvLB)2H zjv!J0O`-zft|0Jv$3k5wScY)XB+9leZgR5%3~HtZA=bCg7=Dn+F}>2lf;!*1+vBtf z9jhmqlH=t5XW{0MC7Y~O7jaju&2`p!ZDLGlgnd~%+EJ%A#pIByi-+EOmoLVoK&ow8 zTDjB%0hxhiRv+O3c2*y00rMA=)s|3-ev7emcbT43#izku7dvaDXy1IMV0ahjB9yzi z9C9fN+I2Mzt1*{`a6B?+PdWHiJ5fH}rb2t>q)~3RfCxmyK^y5jN7Pn(9DFh61GO%p zuBErj=m|bDn_L8SINU)Z&@K*AgGz+SUYO_RUeJt=E0M+eh&kqK;%Y1psBNU<4-s9# ziHFr7QP6Ew=-2CdfA#Bf|EsctH;<&=Hsd>)Ma8NvHB$cpVY@}TV!UN}3?9o@CS5kw zx%nXo%y|r5`YOWoZi#hE(3+rNKLZ2g5^(%Z99nSVt$2TeU2zD%$Q(=$Y;%@QyT5Rq zRI#b><}zztscQaTiFbsu2+%O~sd`L+oKYy5nkF4Co6p88i0pmJN9In`zg*Q;&u#uK zj#>lsuWWH14-2iG z&4w{6QN8h$(MWPNu84w1m{Qg0I31ra?jdyea*I~Xk(+A5bz{x%7+IL}vFDUI-Rf{! zE^&Dau9QxA2~)M98b42(D6Q}2PUum0%g>B?JS?o~VrP+Go2&c-7hIf7(@o1*7k$zS zy@o5MEe8DoX$Ie(%SZByyf9Xf9n8xkoX}s6RiO1sg*kAV^6EAAz$>*x^OmIy!*?1k zG+UQ|aIWDEl%)#;k{>-(w9UE7oKM#2AvQud}sby=D7$l6{$}SE8O9WgHM_+ zJ?tHeu@Pi93{AuwVF^)N(B~0?#V*6z;zY)wtgqF7Nx7?YQdD^s+f8T0_;mFV9r<+C z4^NloIJIir%}ptEpDk!z`l+B z5h(k$0bO$VV(i$E@(ngVG^YAjdieHWwMrz6DvNGM*ydHGU#ZG{HG5YGTT&SIqub@) z=U)hR_)Q@#!jck+V`$X5itp9&PGiENo(yT5>4erS<|Rh#mbCA^aO2rw+~zR&2N6XP z5qAf^((HYO2QQQu2j9fSF)#rRAwpbp+o=X>au|J5^|S@(vqun`du;1_h-jxJU-%v| z_#Q!izX;$3%BBE8Exh3ojXC?$Rr6>dqXlxIGF?_uY^Z#INySnWam=5dV`v_un`=G*{f$51(G`PfGDBJNJfg1NRT2&6E^sG%z8wZyv|Yuj z%#)h~7jGEI^U&-1KvyxIbHt2%zb|fa(H0~Qwk7ED&KqA~VpFtQETD^AmmBo54RUhi z=^Xv>^3L^O8~HO`J_!mg4l1g?lLNL$*oc}}QDeh!w@;zex zHglJ-w>6cqx3_lvZ_R#`^19smw-*WwsavG~LZUP@suUGz;~@Cj9E@nbfdH{iqCg>! zD7hy1?>dr^ynOw|2(VHK-*e%fvU0AoKxsmReM7Uy{qqUVvrYc5Z#FK&Z*XwMNJ$TJ zW1T**U1Vfvq1411ol1R?nE)y%NpR?4lVjqZL`J}EWT0m7r>U{2BYRVVzAQamN#wiT zu*A`FGaD=fz|{ahqurK^jCapFS^2e>!6hSQTh87V=OjzVZ}ShM3vHX+5IY{f^_uFp zIpKBGq)ildb_?#fzJWy)MLn#ov|SvVOA&2|y;{s;Ym4#as?M^K}L_g zDkd`3GR+CuH0_$s*Lm6j)6@N;L7Vo@R=W3~a<#VxAmM&W33LiEioyyVpsrtMBbON+ zX^#%iKHM;ueExK@|t3fX`R+vO(C zucU#Xf>OjSH0Kd%521=Sz%5Y!O(ug(?gRH@K>IUayFU~ntx`Wdm27dB-2s@)J=jf_ zjI-o;hKnjQ|Lg~GKX!*OHB69xvuDU zuG-H48~inKa)^r539a{F)OS`*4GShX>%BR)LU~a-|6+sx&FYsrS1}_b)xSNOzH|Kv zq>+1-cSc0`99EsUz(XWcoRO)|shn>TqKoQBHE)w8i8K`*Xy6(ls%WN_#d}YC^)NJ; zzl8!Zduz^Gg8*f0tCWnLEzw6k5Fv!QWC1x4)3r}+x~@#O8_)0>lP-@3(kFwLl%%Mz(TpATVnL5Pl2Gahw45QXI~>Hrw))CcEs@PP?}4^zkM$ z@(?H6^`Jl?A=(&Ue;W0`*a8&fR7vde@^q^AzX^H#gd~96`Ay^_A%?;?@q@t7l7iGn zWms#2J|To4;o1?3g3L!K_chdtmbEg~>U>$5{WO@Ip~YE&H($(^X6y_OBuNHkd0wu= z4rXGy#-@vZ?>M<_gpE8+W-{#ZJeAfgE#yIDSS?M?K(oY@A|FaS3P;OjMNOG% zGWyZWS(}LJCPaGi9=5b%sq$i!6x@o(G}wwfpI5|yJe24d_V}cT1{^(Qe$KEMZ;>I@ zuE6ee%FLgem>CKEN8SeY)fpK#>*lGcH~71)T4p|9jWT;vwM@N!gL}nCW=Oi6+_>K2 zl4sWXeM1U}RETA~hp=o3tCk+?Zwl#*QA>Wwd|FlUF0)U;rEGPD1s0Syluo zfW9L(F>q9li8YKwKXZrp*t)N9E;?&Hdbm-AZp2BcDTHO6q=tzVkZsozEIXjIH`tm} zo2-UleNm*Lj7zgvhBph_|1IggkSuW~S(9ueZEfao8BuzqlF(a+pRivTv(Zb zXFaHwcuovdM#d+!rjV7F<^VW&@}=5|xj!OUF)s0zh|8yzC)7!9CZB+TLnycoGBsDF z$u&j={5c(4A$iik;x6_S96Krw8--+9pGY+*oSVTIuq;$z8*)W8B~rMX_(U6uM}!Gc`T;WfEKwI84%)-e7j}>NA(O_)3Vn9 zjXxY1Fnx3Fx%CFpUHVu0xjvxgZv}F9@!vC!lD|05#ew3eJ}@!V&urwRKH`1f{0e^o zWvM1S@NbI6pHdzm33pza_q;#?s%J*$4>10uYi4l%5qi|j5qh+D=oqSJR=7QwkQh>>c$|uJ#Z@lK6PMHs@ zyvnnoOSkGQkYz#g>||xN&1fV)aJb*y--Y`UQV~lt!u8yTUG59ns1l7u>CX2F>9fl; zB)zH3z^XHmSU{F_jlvESvaNL&nj^;j)29~1LcTYw>(6}>bt0hiRooqm0@qTj%A&P9 zKmexPwyXG@Rs1i+8>AJ;=?&7RHC7Mn%nO>@+l?Qj~+lD376O2rp)>tlVHn8MKq zwop1KRLhUjZ|+6ecGIAftSPT*3i94=QzYCi_ay+5J&O(%^IsqZ!$w-^bmd7ds$^!q z;AkC;5mTAU>l0S$6NSyG30Ej?KPq@#T)^x#x?@U~fl2m$Ffk)s6u|iPr!)-j0BlA7p3E*A|My8S#KH;8i-IQq7Q*F4*ZVPe<{^SWz_ zr?!6cS+@|C#-P~d#=W1n7acn8_pg#W-lcyf+41zwR+BU6`jUkP^`*wgX)FxEaXzoi z8)?FE*97Yqz|b@fR1(r{QD363t260rQ(F||dt9^xABi+{C*_HL9Zt5T;fq|#*b}=K zo5yj_cZB(oydMAL&X(W6yKf>ui?!%(HhiHJ83EA|#k0hQ!gpVd( zVSqRR&ado+v4BP9mzamKtSsV<|0U-Fe2HP5{{x&K>NxWLIT+D^7md{%>D1Z-5lwS~ z6Q<1`Hfc+0G{4-84o-6dr@)>5;oTt|P6jt9%a43^wGCslQtONH)7QXJEYa!c~39 zWJpTL@bMYhtem1de>svLvOUa*DL7+Ah0(_~2|ng`!Z!qiN}6xL;F}<%M8qWv&52-Y zG*1A&ZKlp~{UFV%Hb_*Re({93f7W*jJZMV-Yn|<+l3SPN+%GuPl=+tSZxxr%?6SEc zntb0~hcK691wwxlQz_jSY+V_h+0o`X!Vm{;qYK$n?6ib1G{q>a%UejzOfk6q<=8oM z6Izkn2%JA2E)aRZbel(M#gI45(Fo^O=F=W26RA8Qb0X;m(IPD{^Wd|Q;#jgBg}e( z+zY(c!4nxoIWAE4H*_ReTm|0crMv8#RLSDwAv<+|fsaqT)3}g=|0_CJgxKZo7MhUiYc8Dy7B~kohCQ$O6~l#1*#v4iWZ=7AoNuXkkVVrnARx?ZW^4-%1I8 zEdG1%?@|KmyQ}tploH>5@&8Cp{`)CxVQOss&x|Z7@gGL3=tCVNDG!N9`&;N$gu^MDk|`rRm=lhnXAJ5v1T)WTz)qvz|Dw zR?{}W4VB(O6#9%o9Z^kFZZV*PDTAWqkQ8TH!rti8QIcR&>zcg3qG}&A( zwH^K8=`1C1lRfhrX{IvNn9R9!$UMC%k(;;VH%`S0h_on|Gh6qDSH&#}*m-u{;p~WB zF$_I~xx!RxVrxNQdr@3T>{F#^D{@N9OYC9LsV62F_Z1KYQ5yk*C5WQ4&q}Kz(I{9UWWf?LIcCZicB1EO_FUH*a9QKS(4IR%#D5DTi_@M}Q_-4)J4d zz@!vR0}5MPAOK(#uL+$7XOcP$5SS#*EK9Rt6XN%}HB7@`8S^gNRk!HLv(CvCjX4o= z>9scPwWbE!F8T=@x9^;s-OF2!eO(!gL9$-AmzUiDnu&QS4If5ea2T070n1-IyNhck z9$J8b!he3@q5qB-cQ;5ymVIXXn46kK0sqKZV+3s3^mac=3~BrCW})WNrrRs1KtMmg zLzwXYC?@_H#s3W4D$W0rh%WL|G<1$$uYdptPbxy0ke!c%v#x9I=2?S)YVkg1X$W^cB!i>B{e9wXlm8AcCT8|verIZQngj>{%W%~W0J%N`Q($h z^u3}p|HyHk?(ls7?R`a&&-q@R<94fI30;ImG3jARzFz<(!K|o9@lqB@Va+on`X2G) zegCM8$vvJ$kUwXlM8df|r^GQXr~2q*Zepf&Mc%kgWGTf;=Wx%7e{&KId-{G}r22lI zmq%L6Y-M*T$xf8 z#kWOBg2TF1cwcd{<$B)AZmD%h-a6>j z%I=|#ir#iEkj3t4UhHy)cRB$3-K12y!qH^1Z%g*-t;RK z6%Mjb*?GGROZSHSRVY1Ip=U_V%(GNfjnUkhk>q%&h!xjFvh69W8Mzg)7?UM=8VHS* zx|)6Ew!>6-`!L+uS+f0xLQC^brt2b(8Y9|5j=2pxHHlbdSN*J1pz(#O%z*W-5WSf# z6EW5Nh&r<;$<3o1b013?U$#Y!jXY)*QiGFt|M58sO45TBGPiHl4PKqZhJ|VRX=AOO zsFz-=3$~g#t4Ji9c;GFS9L~}~bzgCqnYuJ-60AMDdN7HZt8_$~Of{oXaD3HVn9zkH z`>#xQNe=YpWTq_LcOoy}R`L<_4il7w4)QH4rl?AUk%?fH##I>`1_mnp&=$-%SutYT zs}sSNMWo;(a&D()U$~PG0MvZ#1lmsF&^P4l_oN#_NORD-GSmR{h_NbJ^ZdY#R9#qW zKAC%V*?y~}V1Zh#d|-z1Z8sy5A+}*cOq$xk@Pn&{QffzG-9ReyPeEhqF%~Z3@|r(s z3(wA&)dV~fELW*&*=!~l9M=7wq8xE(<@)BjjN8bUiS8@N9E{wi+Dd!V1AtT;Nl}9> zTz`2ge2Jn#Dlg1kC%oFlOe<>?jYC`Asr^%i4hH;S`*qZTPRan2a9Kjj=0aq{iVi2Z z87PZt$d(LAm_{92kl+2Z%k3KGV;~gsp;C>k?gMYZrVIzaI|0D+fka9G_4v>N96*8T zI(C8bj?A7l%V&U?H_IpSeCvf7@y1e?b>G7cN382GVO0qAMQ93(T*<*9c_;%P1}x2l zi8S$s<=e_8ww%DaBAf4oIQ7}U7_48$eYpo}Fb+F|K|43IAPR1y9xbqPPg6er{I7xj|=>-c%pGBRLn1~=5KbAb1mJAx=z(loN!w{49VkEthF>*OX z)=gqXyZB5%5lIWYPWh~{!5pSt43-)-@L@x=pmiuKP-3Cwq8qSxGNwaTT4->BWEjxk zUjr)z7WrBZB5u3iV>Y_>*i~*!vRYL)iAh5hMqNzVq1eeq=&d9Ye!26jks{f~6Ru&c zg$D;^4ui#kC`rSxx`fP!zZ^6&qSneQzZRq0F*V4QvKYKB<9FC%t#)Tik%Zq*G*IOW z3*`2!4d)!3oH>GxVcXlorJDt+JnH)p{~olYBPq|>_V@8=l#(f*diW=L+%>rfWCcPQ z#H^ksQt15Z5Uc4ODq8_JwD5^H&OGqyH6E@MabJQO>s`?bqgA6}J_QpytW{2jH#eCN z8k7y*TFZ2lj2B|1CB(@QZedFfPhX|IQbKMI;$YK>9Zla0fsU7}an6(kP;sXpBWLR` zJ#z_kk!`JJC7h(1J!+G)gL2WB2&0*~Q!%s??}GH?=`hU@03xOwU} z6s7?tGySLz!%(MwxQRiF)2(vR2wQX`YB}u&I-S+RR)LQcyH407#-{*pWLJJR?X|5 zsAl2k{&0N-?JArn@)9YTo-5+gl}R~XkbZM*5AOjPrcikpE3P?p0oN^?H+5+n)}Qxe z*RQ!-eu0RxPyF8B=}xnseNpQMXFU$d^=(G%kUd&|!BHSm7bXoGR$WA+%yjuA{|S>u z?9N6JDhS+ui~rd?wY_t7`p)|qKIMM>6jz%$jv4hc_YUDjF6-%5muq|SNuoji2)|qK zNY5+oWMe+5vu{I*grk6xlVk;(J)uuy13G`VDbj(~Vz9lA)_;$aj?=-cmd#h~N0mn{ z9EIS_d4C=L3H;Pl^;vcpb&-B+)8vt%#?gn5z>#;G{1L&8u8cXJYADMUsm9>%*%)&F zsi&I{Y=VUsV82+)hdNgDWh^M7^hMs|TA0M269^|RIGfdX1MetV2z`Ycb&_Mn4iRI! zeI6O}O9mOhN6pzfs5IfMz#Gxl`C{(111okA8M4gijgb~5s7QTyh84zUiZZ^sr1^ps z1GO`$eOS@k@XP^OVH|8)n}Wx)fKHoGwL&5;W?qEf5Jdsd!3hf7L`%QNwN0gGBm^2= z@WI+qJMJG1w2AS9d@Dt$sj_P$+S2kh7+M72^SfcdBjQEtWQ5?PT&a~G9hOo6CtS>h zoghqoR;sk{X)`ZK-M|lu{M}0>Mrs^ZW@ngC?c$26_vYKDBK^n7sFiod_xV#XcPL!^ zRPyqD{w^9u{oA3y73IW0 zH;%xop$r(Q=bq=JaLT%myEKD_2&?L@s6TzsUwE#g^OkiU6{lN)(7I?%a;_%r5_^@d zS-Z)Q-2o|~?F~f`sHlhNhiZk;!CW;3Ma6{xPlBjJx8PXc!Oq{uTo$p*tyH~ka`g<` z;3?wLhLg5pfL)2bYZTd)jP%f+N7|vIi?c491#Kv57sE3fQh(ScM?+ucH2M>9Rqj?H zY^d!KezBk6rQ|p{^RNn2dRt(9)VN_j#O!3TV`AGl-@jbbBAW$!3S$LXS0xNMr}S%f z%K9x%MRp(D2uO90(0||EOzFc6DaLm((mCe9Hy2 z-59y8V)5(K^{B0>YZUyNaQD5$3q41j-eX))x+REv|TIckJ+g#DstadNn_l~%*RBSss_jV3XS&>yNBc8H2jo(lwcLz-PuYp< z7>)~}zl$Ts0+RFxnYj7-UMpmFcw_H zYrsXM>8icD)@Iauiu_(Y#~Iyl)|pj@kHkWvg2N$kGG(W>Y)nfNn%z2xvTLwk1O2GQ zb^5KAW?c%5;VM4RWBy}`JVCBFOGQWoA9|+bgn7^fY3tSk1MSZccs9&Fy6{8F>_K@? zK(z=zgmq1R#jGE^eGV`<`>SP9SEBx!_-Ao|VZq6)-rUpd^<2GgVN&uHiM{0zA9kI( z<1^1%*uE$?4mXV@?W8}fvnBOpfwCo^?(a0E402!pZi&Kd5pp$oV%2Ofx<}YC-1mynB3X|BzWC_ufrmaH1F&VrU&Gs+5>uixj*OJ*f=gs9VR8k^7HRR$Ns|DYBc*Slz>hGK5B1}U+}#j0{ohGC zE80>WClD5FP+nUS?1qa}ENOPb2`P4ccI<9j;k?hqEe|^#jE4gguHYz-$_BCovNqIb zMUrsU;Fq%n$Ku_wB{Ny>%(B&x9$pr=Anti@#U%DgKX|HzC^=21<5Fn6EKc#~g!Mcj zJrI(gW+aK+3BWVFPWEF*ntHX5;aabHqRgU-Nr2t++%JRPP7-6$XS|M8o&YSgf3a9A zLW*tSJxoe1?#T4EocApa*+1kUIgy7oA%Ig9n@)AdY%)p_FWgF-Kxx{6vta)2X1O5y z#+%KQlxETmcIz@64y`mrSk2Z17~}k1n{=>d#$AVMbp>_60Jc&$ILCg-DTN~kM8)#o$M#Fk~<10{bQ>_@gU2uZE z*eN~mqqQC*wh{CI(!xvRQ^{jyUcvE~8N)S0bMA^SK@v;b7|xUOi63X~3Qc>2UNSD1) z7moi9K3QN_iW5KmKH>1ijU41PO>BvA6f1;kL)6io%^r>?YQ#+bB;)Rzad5;{XAJGeAT#FnDV0$w2>v|JeFIB zZ>8vmz?WVs78PuCDiHfb@D0Yi;2#%){*#?bY4dpta6dSjquGLcOw?Z{nxg98mN^4* zj&^!WMUQ_zFp+}B|G0vcNsk8(2u9(LAPk5ogKt%zgQ4^1#UCd;`-W#X8v{YyQ_m9g z8`jydw>>@1J{Q*q#5^cHVA~xR9LR3Hl@^bx)`IBKmj+Gmye36;xwL0>sS|mV+$~%b zC;2wEm&Ht3#6P|2Y0XQ+5t-aI)jn{o%&ZHWvjzEtSojFgXxNKO^e(RmM`gsJ4GrR8 zKhBtBoRjnH`mD$kT;-8ttq|iw?*`7iTF_AX<^Qe3=h8L^tqz$w$#Z@Z$`C579Jeeu ztr0z~HEazU&htfG@`HW!201!N(70hCd{%~@Wv)G*uKnJZ8>hFx`9LnYs;T>8p!`5T zx#aXXU?}B{QTV_Ux(EMzDhl-a^y^f5tRU;xnOQoN)pThr4M>-HU)As8nQ34-0*sab&z<2ye-D_3m&Q`KJJ|ZEZbaDrE%j>yQ(LM#N845j zNYrP)@)md;&r5|;JA?<~l^<=F1VRGFM93c=6@MJ`tDO_7E7Ru zW{ShCijJ?yHl63Go)-YlOW2n3W*x%w||iw(Cy>@dBJHdQl){bBVg{wmRt{#oXb9kaWqe{bJPmGE$$ z_0=cmD9dVzh<8&oyM8rK9F^bufW$Bj2cFhw&f*oKKyu$H{PI=Aqe^NL6B=dkMEAk& zE3y&F=x;e|!7kMn%(UX>G!OE$Y$@UyME#d;#d+WLmm@W@y!sboiIox^DZPB|EN<>7 z57xm5YWlFUGyF|{<*;b&Cqm+|DC8{rB9R@2EFHGL^NX*l#AcDpw6}bCmhY7!(Gv{s zm^eYNvzyJLQA#GhmL*oSt^Uulb5&ZYBuGJTC>Vm9yGaZ=Vd--pMUoDRaV_^3hE9b*Pby#Ubl65U!VBm7sV}coY)m zn1Ag^jPPLT93J{wpK%>8TnkNp;=a@;`sA7{Q}JmmS1bEK5=d@hQEWl;k$9M-PYX~S zayGm;P(Wwk23}JR7XM~kNqba`6!Z+Wt2|5K>g_j3ajhR>+;HF?88GBN!P; zr6sQ8YYpn%r^gbi8yYK7qx6U5^Tf<|VfcR$jCo`$VMVh_&(9w@O?|o3eRHq*e*#P z8-==G)D?vB3Zo~b-dkx8lg0^=gn`9FUy?ZzAfWQd>>@cyqF!sHQ_S&@$r&tTB~Lxq zAjAZTK~?J{A|L3)8K>S{`Qf%131B>?<~t=w!D{;olQ>#31R#{go`a9DOy+H*q5t+; z^*Ka!r@#8tk?~tQbylaG-$n#wP2VzIm3vjrZjcmTL zl`{6mhBhMKbSWoGqi;g3z1@G0q!ib`(Zz_o8HG_*vr8U5G|vhZn26h`f~bO&)RY0; zw(CWk*a_{ji_=O9U}66lI` zCm32)SEcAo5)5k>{<8DLI@Zz)*R29BB!^wF;WZRF9sAi39BGObmZzg?$lUn6w1rYPHSB^L4^AN zLObEaUh7TXpt6)hWck#6AZV(2`lze<`urGFre|>LUF+j5;9z%=K@&BPXCM)P$>;Xc z!tRA4j0grcS%E!urO^lsH-Ey*XY4m&9lK(;gJOyKk*#l!y7$BaBC)xHc|3i~e^bpR zz5E-=BX_5n8|<6hLj(W67{mWk@Bfc){NGAX z5-O3SP^38wjh6dCEDLB#0((3`g4rl}@I(&E8V2yDB=wYhSxlxB4&!sRy>NTh#cVvv z=HyRrf9dVK&3lyXel+#=R6^hf`;lF$COPUYG)Bq4`#>p z@u%=$28dn8+?|u94l6)-ay7Z!8l*6?m}*!>#KuZ1rF??R@Zd zrRXSfn3}tyD+Z0WOeFnKEZi^!az>x zDgDtgv>Hk-xS~pZRq`cTQD(f=kMx3Mfm2AVxtR(u^#Ndd6xli@n1(c6QUgznNTseV z_AV-qpfQ0#ZIFIccG-|a+&{gSAgtYJ{5g!ane(6mLAs5z?>ajC?=-`a5p8%b*r*mOk}?)zMfus$+W~k z{Tmz9p5$wsX1@q`aNMukq-jREu;;A6?LA(kpRut+jX?Tt?}4HGQr}7>+8z4miohO2 zU4fQ?Y8ggl%cj&>+M+)TTjn8(?^%`~!oAt#ri8gIbzIig$y#d7o##077fM9sCu%N9 zOIsq4vyox6`itu*j{eOD<$gTZd-$JuyM^cM>{?v<8# zS1yN%R0zRy&>+D*Gv-&S80?JF+Y|c^^IJWDnfy06MI2{NFO-x4JXsb@3Qp;EnL!a{ zJwKwV@mO zYVGvNmeJ!;+ce+@j@oo-+`DaPJX|h@7@4BD`QEdP?NKkYzdIa3KrZt%VUSsR+{b+| zk?dSd#9NnVl?&Y$A{-OtZ>wk%mWVF5)bf`)AA2{EFapIS4jil69Xan>*J^6Juou&`oJx|7-&|@8z?$ z2V#jm!UHstCE*qM{OGtqYY8q+x%SL6&aGY!a>@d=_G~^0;+7dY9P`oJ*)67*9Kx*O zKitC5V3g5;&L-fa37?eN=;V_c^L-ph_uKv5)Q`&!Z!RPlDWA2{J%a2q@_*?-cn@bH zIt)+mA@HaJj2RV+-MNc#y#Vji*N~m!ZyrYyg-7UK4PYK4F7Y$3Y%@Lk6iPp=I96N> z!;ih(KtZMB23*v{`5cJ}^4D*P!k1&OfU&1%borv_q|7jfaV7fL+wwx8Zp*b}B_O>NRSeJeM zpvw3M`=vSYjFYQ11kx1xqOnJ@degPh&SyXnWz-l719EiW17Yo?c~Bh~;R$MOl+jzV zM1yTq-1**x-=AVR;p0;IPi`#=E!G5qIT>EFE`Bn<7o*8!aVd7?(CZT=U9^Gi3rmWUQG z0|GaP9s$^4t_oLCs!fInyCoB(d?=tZ%%Bb2Y+X&7gvQ6~C4kU%e$W_H;-%XSM;&*HYYnLI z>%{5x_RtSUC~PI4C0H^>O%FixKYVubA>#72wexd}Cgwuw5ZYTvcN2ywVP(dO=5975 zCjo)mOa2Bo&ucEsaq8wi1{h*brT(H=XrTOy*P>?0%VV1QDr09X+Je!T)JT`02?gjX zT@B8}h|;4lH35Guq2gKZT?ags-~Ts~S=poPnQ_T1*?U|{$jaur_PjQ6WmF_(XLFG)d#|iiBC=&B zp}1eOQvQ!3UpL?K`=8hAzMkv#a^COr`J8i}d!BPX&*xp-LL#qse~mOtxI-}{yPRNV zJNTL1{7A55F~K>0e&Os%MwQ~?n1>QV=j!8o_`^-&*E|Q-L9DNr%#6sw8kQVE3E|*}$aAoO$@27ei1w=+zU%?AA!;mf#!%IV*w_D=u516!Kz1F0-WnyVB`I6F1Pc3r1=0iT<_(pCyk>@22z1$w$@M>7AIuk6+ zRG&MFVQ_7>5DLoR5HeOa$?2SA(v2u!#8;5I(ss%=x9U#R zU62n~&)22RTTsp${}6C&$+l&0skFVX%ACgc$(iQ#DVRRz!`Y+b>E?;ib(TH#6Wa=} zs(q_;SA|fhyEo7Ix%rAY9j=Ul^Rzd`3ABf+yO@~h@Rh=wo`?;8PdHE1AUo34r7izy znAr`;VavQueSu7bD5r^nXTERcW(P-{2SOSfF1x0cW1Nczvj0}@!!upORN1%_-b2bh zGt#zokJz&SveJRzlUK4DruxR(YuHEAmB%F}buU`*pAzJ7Mbgs4sg;H@&6x*wxvGm6 z>KH@ilsvvdl@CGfm4T+$agodrB=md8ygG!|O=r@FY>S_zX%*)mqf?XBX*chhQ9uPP z-(T(24)})vWD*{bQM5_hy3CD8C>anuNtCXMkG7T?Yew^>=PK!~Hlr0{-0h0cNAJ8> zRMzLFz7aJv)Yh)_s)^L&L*nDV@qfeg>_<`z1z(?s}}3tE4h|7_taB> zPfmmOCFZ8%>`gyf1@|7t3;e~mwBRCDDw(Rrt>@O}obs#1?!W((+9>d$b7t!{&wR!P ziQbn0@j=&sw={`s##Uc@uS^(tbShjtsk=qrU1LW0lu}BplIfzv{fwxNsSaG~b|ryo zTQ}YXfp6o?^sSHW>s~m;l@h6wFbIPw{Z(IqO1u){{hEZgrTdF0o$n;hYIm`h5ejym zWt^w~#8p1J)FtfY6LvGmNQ~#n>4#mN4B^ zjrQk)Zt%k}GBRD>l`<~og6N_{6HYKDtsAtd%y?KbXCQR(sW8O(v_)kwYMz|(OW zsFz6A1^abSklOl`wLC-KYI8x=oMD^qZBs}}JVW@YY|3&k&IZ_n2Ia@5WiK>buV!E- zOsYcS4dFPE7vzj%_?5i2!XY`TiPd*jy>#C`i^XG8h?f35`=)s`0EhQBN!+YrXbpt( z-bwg_Jen`w<+6&B`hldU%rr&Xdgtze>rKuJ61AI12ja-eDZZX-+u1H>Sa|7pCine9 z&MEhmT7nq`P!pPK>l?I8cjuPpN<7(hqH~beChC*YMR+p;;@6#0j2k$=onUM`IXW3> z`dtX8`|@P|Ep-_0>)@&7@aLeg$jOd4G`eIW=^dQQ*^cgKeWAsSHOY?WEOsrtnG|^yeQ3lSd`pKAR}kzgIiEk@OvQb>DS*pGidh`E=BHYepHXbV)SV6pE2dx6 zkND~nK}2qjDVX3Z`H;2~lUvar>zT7u%x8LZa&rp7YH@n@GqQ65Cv+pkxI1OU6(g`b z?>)NcE7>j@p>V0mFk-5Rpi`W}oQ!tUU&Yn8m0OWYFj|~`?aVFOx;e`M)Q!YSokY)3 zV6l-;hK6?j=mp2#1e5cCn7P6n_7)n^+MdRw@5pvkOA>|&B8`QZ32|ynqaf}Kcdro= zzQchCYM0^)7$;m2iZnMbE$!}hwk&AVvN`iX3A9mB&`*BDmLV-m`OMvd`sJ?;%U`p~ zmwow{y6sPbcZNQPZ#GQS0&mzy?s%>_p>ZM|sCXVAUlST;rQ-3#Iu!-bpFSV4g7?-l zGfX>Z#hR+i;9B};^CO@7<<#MGFeY)SC&;a{!` zf;yaQo%{bjSa8KT~@?O$cK z(DGnm7w>cG1hH#*J%X}%Y%~+nLT*{aP08@l&Nu}>!-j|!8lSqt_xUNF+Y}SQmupyb zPua2PI;@1YaIsRF*knA^rJv84Tc=7?J2}!1kMfHSO$d$+PK*u?OI%=P7;`PHxMB0k zau~T0Wk)rPEGJ$NiXW~kfPA#m%Sr|7=$tHelF9A6rFLa$^g{6)8GSW*6}#~Zb^qk% zg=pLwC!SkY+&Gne((9`TCy`i`a#eCS{A2yMi>J>p*NS*!V~aAgK;wnSOHPULqzyj- z-q4BPXqXn))iRnMF*WZj17wUYjC!h43tI7uScHLf1|WJfA7^5O9`%lH>ga`cmpiz( zs|I8nTUD4?d{CQ-vwD!2uwGU_Ts&{1_mvqY`@A{j^b?n&WbPhb418NY1*Otz19`1w zc9rn?0e_*En&8?OWii89x+jaqRVzlL!QUCg^qU&+WERycV&1+fcsJ%ExEPjiQWRTU zCJpu*1dXyvrJJcH`+OKn7;q`X#@Gmy3U?5ZAV~mXjQhBJOCMw>o@2kznF>*?qOW;D z6!GTcM)P-OY-R`Yd>FeX%UyL%dY%~#^Yl!c42;**WqdGtGwTfB9{2mf2h@#M8YyY+!Q(4}X^+V#r zcZXYE$-hJyYzq%>$)k8vSQU` zIpxU*yy~naYp=IocRp5no^PeFROluibl( zmaKkWgSWZHn(`V_&?hM{%xl3TBWCcr59WlX6Q{j45)`A^-kUv4!qM=OdcwpsGB)l} z&-_U+8S8bQ!RDc&Y3~?w5NwLNstoUYqPYs(y+lj!HFqIZ7FA>WsxAE7vB=20K zn_&y{2)Uaw4b^NCFNhJXd&XrhA4E~zD7Ue7X^f98=&5!wn_r=6qAwDkd>g#2+*ahd zaV|_P_8e%jiHh7W;cl(d=&-r-C}_Ov?bts8s^rKUWQ|XkuW!ToSwe}Z{4|kl+q&&W zn%iW48c5*ft#*m)+xSps+j(B5bPh&u0&m6=@WgwBf_QfJJzg2Qdz89HwcV`5kZ#5z zw;W&H8>5R(>KRwvd0gh30wJHA>|2N(im;~wy1HTv_}Ue%qb)>5qL^$hIyPvoT(nk_<`7F;#nS8;q!cqKspvBc<%xMsQj*h|>`Z)F6LDxue@to))OIbs2X+zY2L9#2UNrR^)?c8&PFc?j*&Q-r|C%7a$)ZRQ->#|?rEj&M4spQfNt;J^ntwf(d+q;tt)C`d{*|t)czD4x-qw{Chm0vuKp8axqy5`Yz z1756|;JX1q(lEieR=uT;%havqflgv+`5i!Z`R}(JNV~&`x}I9Lmm;aB7Bnc^UC?>W zu)(J7@fs}pL=Y-4aLq&Z*lO$e^0(bOW z3gWbcvb^gjEfhV=6Lgu2aX{(zjq|NH*fSgm&kBj?6dFqD2MWk5@eHt@_&^ZTX$b?o}S<9BGaCZIm6Hz)Qkruacn!qv*>La|#%j*XFp(*;&v3h4 zcjPbZWzv|cOypb@XDnd}g%(@f7A>w2Nseo|{KdeVQu)mN=W=Q`N?ID%J_SXUr0Rl# z3X;tO*^?41^%c!H;ia@hX``kWS3TR|CJ4_9j-?l6RjC=n?}r&sr>m%58&~?$JJV6{ zDq5h#m4S_BPiibQQaPGg6LIHVCc`9w3^3ZVWP$n>p7 z5dIEH-W9e;$Id8>9?wh%WnWf>4^1U<%vn=<4oNFhVl9zVk+jn;WtQUQ)ZeEjKYy8C z3g#tIb28thR1nZdKrN}(r zJdy-Y3Rvr5D3D|msZbmE;FLePbiM0ZjwTIQQHk)8G+sB$iwmEa2kQv&9Vs9m#$_8j zNKz}(x$Wc(M)a9H-Pn?5(Lk-CmOS(&+EVLOfsiq>e3ru6P?Lp>FOwPt>0o=j8UyF^ zO{(vf#MGx^y~WaOKnt%I78s}60(O#jFx0^47^Ikh$QTar(Dg$c=0KR|rRD|6s zz?tEX0_=(Hm0jWl;QOu!-k)mV?^i(Etl=Lg-{ z0G}CBprLX60zgAUz-fS^&m#o;erEC5TU+mn_Wj(zL$zqMo!e`D>s7X&;E zFz}}}puI+c%xq0uTpWS3RBlIS2jH0)W(9FU1>6PLcj|6O>=y)l`*%P`6K4}U2p}a0 zvInj%$AmqzkNLy%azH|_f7x$lYxSG=-;7BViUN(&0HPUobDixM1RVBzWhv8LokKI2 zjDwvWu=S~8We)+K{oMd-_cuXNO&+{eUaA8Ope3MxME0?PD+0a)99N>WZ66*;sn(N++hjPyz5z0RC{- z$pcSs{|)~a_h?w)y}42A6fg|nRnYUjMaBqg=68&_K%h3eboQ=%i083nfIVZZ04qOp%d*)*hNJA_foPjiW z$1r8ZZiRSvJT3zhK>iR@8_+TTJ!tlNLdL`e0=yjzv3Ie80h#wSfS3$>DB!!@JHxNd z0Mvd0Vqq!zfDy$?goY+|h!e(n3{J2;Ag=b)eLq{F0W*O?j&@|882U5?hUVIw_v3aV8tMn`8jPa5pSxzaZe{z}z|}$zM$o=3-mQ0Zgd?ZtaI> zQVHP1W3v1lbw>|?z@2MO(Ex!5KybKQ@+JRAg1>nzpP-!@3!th3rV=o?eiZ~fQRWy_ zfA!U9^bUL+z_$VJI=ic;{epla<&J@W-QMPZm^kTQ8a^2TX^TDpza*^tOu!WZ=T!PT z+0lJ*HuRnNGobNk0PbPT?i;^h{&0u+-fejISNv#9&j~Ep2;dYspntgzwR6<$@0dTQ z!qLe3Ztc=Ozy!btCcx!G$U7FlBRe}-L(E|RpH%_gt4m_LJllX3!iRYJEPvxcJ>C76 zfBy0_zKaYn{3yG6@;}S&+BeJk5X}$Kchp<Ea-=>VDg&zi*8xM0-ya!{ zcDN@>%H#vMwugU&1KN9pqA6-?Q8N@Dz?VlJ3IDfz#i#_RxgQS*>K+|Q@bek+s7#Qk z(5NZ-4xs&$j)X=@(1(hLn)vPj&pP>Nyu)emQ1MW6)g0hqXa5oJ_slh@(5MMS4xnG= z{0aK#F@_p=e}FdAa3tEl!|+j?h8h`t0CvCmNU%dOwEq<+jmm-=n|r|G^7QX4N4o(v zPU!%%w(Cet)Zev3QA?;TMm_aEK!5(~Nc6pJlp|sQP@z%JI}f0_`u+rc`1Df^j0G&s ScNgau(U?ep-K_E5zy1%ZQTdPn literal 0 HcmV?d00001 diff --git a/kotlin/gradle/wrapper/gradle-wrapper.properties b/kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..2b22d057a07 --- /dev/null +++ b/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/kotlin/gradlew b/kotlin/gradlew new file mode 100755 index 00000000000..65dcd68d65c --- /dev/null +++ b/kotlin/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/kotlin/gradlew.bat b/kotlin/gradlew.bat new file mode 100755 index 00000000000..6689b85beec --- /dev/null +++ b/kotlin/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/kotlin/kotlin-js-store/yarn.lock b/kotlin/kotlin-js-store/yarn.lock new file mode 100644 index 00000000000..537d261cff0 --- /dev/null +++ b/kotlin/kotlin-js-store/yarn.lock @@ -0,0 +1,2020 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" + integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + +"@trustwallet/wallet-core@3.1.10": + version "3.1.10" + resolved "https://registry.yarnpkg.com/@trustwallet/wallet-core/-/wallet-core-3.1.10.tgz#3af4d0021a91f61faea7d3401d4e04197a843a9b" + integrity sha512-4/Du55HQLcNrJsBXWqUlk+5G7TCan1CfaOhRTxfXad1txWMtTj75kkYnbfST5Oi2DaKbuH86FF+yYroSdgoEiw== + dependencies: + protobufjs ">=6.11.3" + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.13" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" + integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.4.10" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.10.tgz#19731b9685c19ed1552da7052b6f668ed7eb64bb" + integrity sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/json-schema@*", "@types/json-schema@^7.0.8": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=13.7.0": + version "18.11.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" + integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== + +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +accepts@~1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn@^8.5.0, acorn@^8.7.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@^1.19.0: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.14.5: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001449: + version "1.0.30001449" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001449.tgz#a8d11f6a814c75c9ce9d851dc53eb1d1dfbcd657" + integrity sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.5.3, chokidar@^3.5.1: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.14: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== + +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.3.4, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +dom-serialize@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +dukat@0.5.8-rc.4: + version "0.5.8-rc.4" + resolved "https://registry.yarnpkg.com/dukat/-/dukat-0.5.8-rc.4.tgz#90384dcb50b14c26f0e99dae92b2dea44f5fce21" + integrity sha512-ZnMt6DGBjlVgK2uQamXfd7uP/AxH7RqI0BL9GLrrJb2gKdDxvJChWy+M9AQEaL+7/6TmxzJxFOsRiInY9oGWTA== + dependencies: + google-protobuf "3.12.2" + typescript "3.9.5" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.284: + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +engine.io-parser@~5.0.3: + version "5.0.6" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.6.tgz#7811244af173e157295dec9b2718dfe42a64ef45" + integrity sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw== + +engine.io@~6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f" + integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA== + dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + +enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +ent@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== + +envinfo@^7.7.3: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +follow-redirects@^1.0.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +format-util@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" + integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3, glob@^7.1.7: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +google-protobuf@3.12.2: + version "3.12.2" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.12.2.tgz#50ce9f9b6281235724eb243d6a83e969a2176e53" + integrity sha512-4CZhpuRr1d6HjlyrxoXoocoGFnRYgKULgMtikMddA9ztRyYR59Aondv2FioyxWVamRo0rF2XpYawkTCBEQOSkA== + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +karma-chrome-launcher@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz#baca9cc071b1562a1db241827257bfe5cab597ea" + integrity sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ== + dependencies: + which "^1.2.1" + +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== + dependencies: + minimist "^1.2.3" + +karma-sourcemap-loader@0.3.8: + version "0.3.8" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.8.tgz#d4bae72fb7a8397328a62b75013d2df937bdcf9c" + integrity sha512-zorxyAakYZuBcHRJE+vbrK2o2JXLFWK8VVjiT/6P+ltLBUGUvqTEkUiQ119MGdOrK7mrmxXHZF1/pfT6GgIZ6g== + dependencies: + graceful-fs "^4.1.2" + +karma-webpack@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.0.tgz#2a2c7b80163fe7ffd1010f83f5507f95ef39f840" + integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + webpack-merge "^4.1.5" + +karma@6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.0.tgz#82652dfecdd853ec227b74ed718a997028a99508" + integrity sha512-s8m7z0IF5g/bS5ONT7wsOavhW4i4aFkzD4u4wgzAQWT4HGUeWI3i21cK2Yz6jndMAeHETp5XuNsRoyGJZXVd4w== + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" + di "^0.0.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.4.1" + source-map "^0.6.1" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log4js@^6.4.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.7.1.tgz#06e12b1ac915dd1067146ffad8215f666f7d2c51" + integrity sha512-lzbd0Eq1HRdWM2abSD7mk6YIVY0AogGJzb/z+lqzRk+8+XJP+M6L1MS5FUSc3jjGru4dbKjEMJmqlsoYYpuivQ== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.3" + +long@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" + integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9" + integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-releases@^2.0.8: + version "2.0.9" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.9.tgz#fe66405285382b0c4ac6bcfbfbe7e8a510650b4d" + integrity sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +protobufjs@>=6.11.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.0.tgz#ca6b1ceb9a9efe21186ba96178089ec563011a5e" + integrity sha512-hYCqTDuII4iJ4stZqiuGCSU8xxWl5JeXYpwARGtn/tWcKCAro6h3WQz+xpsNbXW0UYqpmTQFEyFWO0G0Kjt64g== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +qjobs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.9.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +socket.io-adapter@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" + integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== + +socket.io-parser@~4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206" + integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.4.1: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90" + integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + debug "~4.3.2" + engine.io "~6.2.1" + socket.io-adapter "~2.4.0" + socket.io-parser "~4.2.1" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-loader@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-4.0.0.tgz#bdc6b118bc6c87ee4d8d851f2d4efcc5abdb2ef5" + integrity sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw== + dependencies: + abab "^2.0.6" + iconv-lite "^0.6.3" + source-map-js "^1.0.2" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +streamroller@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.4.tgz#844a18e795d39c1089a8216e66a1cf1151271df0" + integrity sha512-Ha1Ccw2/N5C/IF8Do6zgNe8F3jQo8MPBnMBGvX0QjNv/I97BcNRzK6/mzOpZHHK7DjMLTI3c7Xw7Y1KvdChkvw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@8.1.1, supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.1.3: + version "5.3.6" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" + integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.14" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.14.1" + +terser@^5.14.1: + version "5.16.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.2.tgz#8f495819439e8b5c150e7530fc434a6e70ea18b2" + integrity sha512-JKuM+KvvWVqT7muHVyrwv7FVRPnmHDwF6XwoIxdbF5Witi0vu99RYpxDexpJndXt3jbZZmmWr2/mQa6HvSNdSg== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typescript@3.9.5: + version "3.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36" + integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ== + +ua-parser-js@^0.7.30: + version "0.7.33" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" + integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +vary@^1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webpack-cli@4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" + colorette "^2.0.14" + commander "^7.0.0" + cross-spawn "^7.0.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + webpack-merge "^5.7.3" + +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-merge@^5.7.3: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@5.74.0: + version "5.74.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980" + integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.10.0" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +which@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@~8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0, yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/kotlin/settings.gradle.kts b/kotlin/settings.gradle.kts new file mode 100644 index 00000000000..481134aa1f2 --- /dev/null +++ b/kotlin/settings.gradle.kts @@ -0,0 +1,27 @@ +@file:Suppress("UnstableApiUsage") + +rootProject.name = "WalletCoreKotlin" + +pluginManagement { + repositories { + google() + mavenCentral() + } +} + +dependencyResolutionManagement { + // Uncomment after https://youtrack.jetbrains.com/issue/KT-55620/ + // repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +includeBuild( + "build-logic", +) + +include( + ":wallet-core-kotlin" +) diff --git a/kotlin/wallet-core-kotlin/.gitignore b/kotlin/wallet-core-kotlin/.gitignore new file mode 100644 index 00000000000..1f22b7e6731 --- /dev/null +++ b/kotlin/wallet-core-kotlin/.gitignore @@ -0,0 +1,3 @@ +/src/**/generated/ +/src/**/proto/ +/src/nativeInterop/ diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts new file mode 100644 index 00000000000..08395c50f08 --- /dev/null +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -0,0 +1,130 @@ +@file:Suppress("UnstableApiUsage") + +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackOutput + +plugins { + kotlin("multiplatform") + id("com.android.library") + id("convention.maven-publish") + id("convention.file-generation") +} + +kotlin { + android { + publishLibraryVariants = listOf("release") + } + + val nativeTargets = + listOf( + iosArm64(), + iosSimulatorArm64(), + iosX64(), + ) + + js { + browser { + webpackTask { + output.libraryTarget = KotlinWebpackOutput.Target.COMMONJS2 + } + } + useCommonJs() + } + + sourceSets { + all { + languageSettings { + optIn("kotlin.js.ExperimentalJsExport") + } + } + + val androidMain by getting { + kotlin.srcDir(projectDir.resolve("../../jni/cpp")) + kotlin.srcDir(projectDir.resolve("../../jni/kotlin")) + kotlin.srcDir(projectDir.resolve("src/androidMain/generated")) + } + val commonMain by getting { + kotlin.srcDirs( + projectDir.resolve("src/commonMain/generated"), + projectDir.resolve("src/commonMain/proto"), + ) + + dependencies { + api(libs.wire.runtime) + } + } + val iosArm64Main by getting + val iosSimulatorArm64Main by getting + val iosX64Main by getting + val iosMain by creating { + kotlin.srcDir(projectDir.resolve("src/iosMain/generated")) + + dependsOn(commonMain) + iosArm64Main.dependsOn(this) + iosSimulatorArm64Main.dependsOn(this) + iosX64Main.dependsOn(this) + } + val jsMain by getting { + kotlin.srcDir(projectDir.resolve("src/jsMain/generated")) + + dependencies { + // TODO: Replace with local build + implementation(npm(name = "@trustwallet/wallet-core", version = "3.1.10", generateExternals = false)) + } + } + } + + nativeTargets.forEach { nativeTarget -> + nativeTarget.apply { + val main by compilations.getting + val walletCore by main.cinterops.creating { + includeDirs.allHeaders(rootDir.parentFile.resolve("include/TrustWalletCore")) + } + } + } +} + +android { + namespace = "com.trustwallet.core" + compileSdk = libs.versions.android.sdk.compile.get().toInt() + buildToolsVersion = libs.versions.android.sdk.tools.get() + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + defaultConfig { + minSdk = libs.versions.android.sdk.min.get().toInt() + ndkVersion = libs.versions.android.ndk.get() + + externalNativeBuild { + cmake { + arguments += listOf("-DCMAKE_BUILD_TYPE=Release", "-DKOTLIN=True") + } + } + } + + buildFeatures { + aidl = false + compose = false + buildConfig = false + prefab = false + renderScript = false + resValues = false + shaders = false + viewBinding = false + } + + androidComponents { + beforeVariants { + it.enable = it.name == "release" + } + } + + externalNativeBuild { + cmake { + version = libs.versions.android.cmake.get() + path = rootDir.parentFile.resolve("CMakeLists.txt") + } + } +} diff --git a/kotlin/wallet-core-kotlin/src/androidMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/androidMain/kotlin/com/trustwallet/core/AnySigner.kt new file mode 100644 index 00000000000..4daa57c663b --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/androidMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -0,0 +1,15 @@ +// Copyright © 2017-2023 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 + +actual external fun signImpl(input: ByteArray, coin: CoinType): ByteArray + +actual external fun supportsJsonImpl(coin: CoinType): Boolean + +actual external fun signJsonImpl(json: String, key: ByteArray, coin: CoinType): String + +actual external fun planImpl(input: ByteArray, coin: CoinType): ByteArray diff --git a/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt new file mode 100644 index 00000000000..df9455e9943 --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -0,0 +1,32 @@ +// Copyright © 2017-2023 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 + +import com.squareup.wire.Message +import com.squareup.wire.ProtoAdapter + +object AnySigner { + + fun > sign(input: Message<*, *>, coin: CoinType, adapter: ProtoAdapter): T = + adapter.decode(signImpl(input.encode(), coin)) + + fun supportsJson(coin: CoinType): Boolean = supportsJsonImpl(coin) + + fun signJson(json: String, key: ByteArray, coin: CoinType): String = + signJsonImpl(json, key, coin) + + fun > plan(input: Message<*, *>, coin: CoinType, adapter: ProtoAdapter): T = + adapter.decode(planImpl(input.encode(), coin)) +} + +internal expect fun signImpl(input: ByteArray, coin: CoinType): ByteArray + +internal expect fun supportsJsonImpl(coin: CoinType): Boolean + +internal expect fun signJsonImpl(json: String, key: ByteArray, coin: CoinType): String + +internal expect fun planImpl(input: ByteArray, coin: CoinType): ByteArray diff --git a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt new file mode 100644 index 00000000000..ad6aa3e3888 --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -0,0 +1,19 @@ +// Copyright © 2017-2023 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 + +actual fun signImpl(input: ByteArray, coin: CoinType): ByteArray = + TWAnySignerSign(input.toTwData(), coin.value)!!.readTwBytes()!! + +actual fun supportsJsonImpl(coin: CoinType): Boolean = + TWAnySignerSupportsJSON(coin.value) + +actual fun signJsonImpl(json: String, key: ByteArray, coin: CoinType): String = + TWAnySignerSignJSON(json.toTwString(), key.toTwData(), coin.value).fromTwString()!! + +actual fun planImpl(input: ByteArray, coin: CoinType): ByteArray = + TWAnySignerPlan(input.toTwData(), coin.value)?.readTwBytes()!! diff --git a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt new file mode 100644 index 00000000000..8498cb26061 --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt @@ -0,0 +1,18 @@ +// Copyright © 2017-2023 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 + +import kotlinx.cinterop.COpaquePointer +import kotlinx.cinterop.readBytes +import kotlinx.cinterop.toCValues + +internal fun COpaquePointer?.readTwBytes(): ByteArray? = + TWDataBytes(this)?.readBytes(TWDataSize(this).toInt()) + +@OptIn(ExperimentalUnsignedTypes::class) +internal fun ByteArray?.toTwData(): COpaquePointer? = + TWDataCreateWithBytes(this?.toUByteArray()?.toCValues(), this?.size?.toULong() ?: 0u) diff --git a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/StringExt.kt b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/StringExt.kt new file mode 100644 index 00000000000..2c23168563d --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/StringExt.kt @@ -0,0 +1,17 @@ +// Copyright © 2017-2023 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 + +import kotlinx.cinterop.COpaquePointer +import kotlinx.cinterop.CValuesRef +import kotlinx.cinterop.toKString + +internal fun String?.toTwString(): COpaquePointer? = + TWStringCreateWithUTF8Bytes(this) + +internal fun CValuesRef<*>?.fromTwString(): String? = + TWStringUTF8Bytes(this)?.toKString() diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt new file mode 100644 index 00000000000..61bdabba2bb --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt @@ -0,0 +1,27 @@ +// Copyright © 2017-2023 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. + +import kotlin.js.Promise + +@JsExport +@JsName("WalletCoreKotlin") +object WalletCore { + + internal var Instance: dynamic = null + + fun init(): Promise = + WalletCoreExports.initWasm() + .then { walletCore: dynamic -> + Instance = walletCore + walletCore + } +} + +@JsModule("@trustwallet/wallet-core") +@JsNonModule +internal external object WalletCoreExports { + fun initWasm(): Promise +} diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt new file mode 100644 index 00000000000..7f4260201c7 --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 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 + +import WalletCore + +internal actual fun signImpl(input: ByteArray, coin: CoinType): ByteArray = + WalletCore.Instance.AnySigner.sign(input.toUInt8Array(), coin._value) + .unsafeCast() + .toByteArray() + +internal actual fun supportsJsonImpl(coin: CoinType): Boolean = + WalletCore.Instance.AnySigner.supportsJSON(coin._value) as Boolean + +internal actual fun signJsonImpl(json: String, key: ByteArray, coin: CoinType): String = + TODO() + +internal actual fun planImpl(input: ByteArray, coin: CoinType): ByteArray = + WalletCore.Instance.AnySigner.plan(input.toUInt8Array(), coin._value) + .unsafeCast() + .toByteArray() diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/UInt8Array.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/UInt8Array.kt new file mode 100644 index 00000000000..001d22c0912 --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/UInt8Array.kt @@ -0,0 +1,17 @@ +// Copyright © 2017-2023 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 + +import org.khronos.webgl.get + +internal typealias UInt8Array = org.khronos.webgl.Uint8Array + +internal fun UInt8Array.toByteArray(): ByteArray = + ByteArray(length, ::get) + +internal fun ByteArray.toUInt8Array(): UInt8Array = + UInt8Array(toTypedArray()) diff --git a/tools/install-kotlin-dependencies b/tools/install-kotlin-dependencies new file mode 100755 index 00000000000..e837297f31e --- /dev/null +++ b/tools/install-kotlin-dependencies @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +"$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager --verbose "cmake;3.22.1" "ndk;25.2.9519653" + +yes | "$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager --licenses diff --git a/tools/kotlin-build b/tools/kotlin-build new file mode 100755 index 00000000000..4eb14383f65 --- /dev/null +++ b/tools/kotlin-build @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +pushd kotlin +./gradlew :wallet-core-kotlin:generateFiles +./gradlew :wallet-core-kotlin:assemble +popd diff --git a/tools/kotlin-release b/tools/kotlin-release new file mode 100755 index 00000000000..0eadf3fe30c --- /dev/null +++ b/tools/kotlin-release @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +source $(dirname $0)/library +version=$(wc_read_version) + +echo "Building $version" + +export ANDROID_HOME="$HOME/Library/Android/sdk" + +pushd kotlin +./gradlew :wallet-core-kotlin:generateFiles +./gradlew :wallet-core-kotlin:assemble :wallet-core-kotlin:publish -Pversion="$version" +popd + +echo "Kotlin build uploaded" From 7e81d2f9483d8699bbd44652715814209968e8b7 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 9 Feb 2023 19:57:43 +0100 Subject: [PATCH 098/426] [CoinType]: Add cointype extension for wasm (#2915) --- wasm/src/CoinTypeExtension.cpp | 114 ++++++++++++++++++++++++++++++++ wasm/src/CoinTypeExtension.d.ts | 22 ++++++ wasm/tests/CoinType.test.ts | 14 +++- 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 wasm/src/CoinTypeExtension.cpp create mode 100644 wasm/src/CoinTypeExtension.d.ts diff --git a/wasm/src/CoinTypeExtension.cpp b/wasm/src/CoinTypeExtension.cpp new file mode 100644 index 00000000000..e6e1f0a5e27 --- /dev/null +++ b/wasm/src/CoinTypeExtension.cpp @@ -0,0 +1,114 @@ +#include +#include "WasmString.h" +#include "generated/PrivateKey.h" +#include + +using namespace emscripten; + +namespace TW::Wasm { + class CoinTypeExtension { + public: + TWCoinType mValue; + CoinTypeExtension(TWCoinType value): mValue(value) {} + + auto value() { + return mValue; + } + + auto blockchain() { + return TWCoinTypeBlockchain(mValue); + } + + auto purpose() { + return TWCoinTypePurpose(mValue); + } + + auto curve() { + return TWCoinTypeCurve(mValue); + } + + auto xpubVersion() { + return TWCoinTypeXpubVersion(mValue); + } + + auto xprvVersion() { + return TWCoinTypeXpubVersion(mValue); + } + + auto validate(const std::string& address) { + return TWCoinTypeValidate(mValue, &address); + } + + auto derivationPath() { + return TWStringToStd(TWCoinTypeDerivationPath(mValue)); + } + + auto derivationPathWithDerivation(TWDerivation derivation) { + return TWStringToStd(TWCoinTypeDerivationPathWithDerivation(mValue, derivation)); + } + + auto deriveAddress(WasmPrivateKey* privateKey) { + return TWStringToStd(TWCoinTypeDeriveAddress(mValue, privateKey->instance)); + } + + auto deriveAddressFromPublicKey(WasmPublicKey* publicKey) { + return TWStringToStd(TWCoinTypeDeriveAddressFromPublicKey(mValue, publicKey->instance)); + } + + auto HRP() { + return TWCoinTypeHRP(mValue); + } + + auto P2pkhPrefix() { + return TWCoinTypeP2pkhPrefix(mValue); + } + + auto P2shPrefix() { + return TWCoinTypeP2shPrefix(mValue); + } + + auto staticPrefix() { + return TWCoinTypeStaticPrefix(mValue); + } + + auto chainID() { + return TWStringToStd(TWCoinTypeChainId(mValue)); + } + + auto slip44ID() { + return TWCoinTypeSlip44Id(mValue); + } + + auto SS58Prefix() { + return TWCoinTypeSS58Prefix(mValue); + } + + auto publicKeyType() { + return TWCoinTypePublicKeyType(mValue); + } + }; + + EMSCRIPTEN_BINDINGS(Wasm_CoinTypeExtension) { + class_("CoinTypeExtension") + .constructor() + .function("blockchain", &CoinTypeExtension::blockchain) + .function("purpose", &CoinTypeExtension::purpose) + .function("curve", &CoinTypeExtension::curve) + .function("xpubVersion", &CoinTypeExtension::xpubVersion) + .function("xprvVersion", &CoinTypeExtension::xprvVersion) + .function("validate", &CoinTypeExtension::validate) + .function("derivationPath", &CoinTypeExtension::derivationPath) + .function("derivationPathWithDerivation", &CoinTypeExtension::derivationPathWithDerivation) + .function("deriveAddress", &CoinTypeExtension::deriveAddress, allow_raw_pointers()) + .function("deriveAddressFromPublicKey", &CoinTypeExtension::deriveAddressFromPublicKey, allow_raw_pointers()) + .function("hrp", &CoinTypeExtension::HRP) + .function("P2pkhPrefix", &CoinTypeExtension::P2pkhPrefix) + .function("P2shPrefix", &CoinTypeExtension::P2shPrefix) + .function("staticPrefix", &CoinTypeExtension::staticPrefix) + .function("chainID", &CoinTypeExtension::chainID) + .function("slip44ID", &CoinTypeExtension::slip44ID) + .function("SS58Prefix", &CoinTypeExtension::SS58Prefix) + .function("publicKeyType", &CoinTypeExtension::publicKeyType) + .function("value", &CoinTypeExtension::value); + } +} diff --git a/wasm/src/CoinTypeExtension.d.ts b/wasm/src/CoinTypeExtension.d.ts new file mode 100644 index 00000000000..f0169dffe9e --- /dev/null +++ b/wasm/src/CoinTypeExtension.d.ts @@ -0,0 +1,22 @@ +export class CoinTypeExtension { + constructor(CoinType) + value(): CoinType + blockchain(): Blockchain + purpose(): Purpose + curve(): Curve + xpubVersion(): HDVersion + xprvVersion(): HDVersion + validate(address: string): boolean + derivationPath(): string + derivationPathWithDerivation(derivation: Derivation): string + deriveAddress(privateKey: PrivateKey): string + deriveAddressFromPublicKey(publicKey: PublicKey): string + hrp(): HRP + P2pkhPrefix(): number + P2shPrefix(): number + staticPrefix(): number + chainID(): string + slip44ID(): number + SS58Prefix(): number + publicKeyType(): PublicKeyType +} diff --git a/wasm/tests/CoinType.test.ts b/wasm/tests/CoinType.test.ts index d18fdf4c60e..416949d944c 100644 --- a/wasm/tests/CoinType.test.ts +++ b/wasm/tests/CoinType.test.ts @@ -9,7 +9,7 @@ import { assert } from "chai"; describe("CoinType", () => { it("test raw value", () => { - const { CoinType } = globalThis.core; + const { CoinType, CoinTypeExtension, Blockchain, Purpose, Curve, Derivation, PrivateKey, HexCoding } = globalThis.core; assert.equal(CoinType.bitcoin.value, 0); assert.equal(CoinType.litecoin.value, 2); @@ -18,5 +18,17 @@ describe("CoinType", () => { assert.equal(CoinType.binance.value, 714); assert.equal(CoinType.cosmos.value, 118); assert.equal(CoinType.solana.value, 501); + let val = new CoinTypeExtension(CoinType.solana); + assert.equal(val.blockchain(), Blockchain.solana); + assert.equal(val.value(), CoinType.solana); + assert.equal(val.purpose(), Purpose.bip44); + assert.equal(val.curve(), Curve.ed25519); + assert.isTrue(val.validate("Bxp8yhH9zNwxyE4UqxP7a7hgJ5xTZfxNNft7YJJ2VRjT")) + assert.equal(val.derivationPath(), "m/44'/501'/0'"); + assert.equal(val.derivationPathWithDerivation(Derivation.solanaSolana), "m/44'/501'/0'/0'"); + let data = HexCoding.decode("8778cc93c6596387e751d2dc693bbd93e434bd233bc5b68a826c56131821cb63") + const key = PrivateKey.createWithData(data); + let addr = val.deriveAddress(key); + assert.equal(addr, "7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q") }); }); From 3d2c86d200ea4402262fdce44032c10a88ec7909 Mon Sep 17 00:00:00 2001 From: Maxim Pestryakov Date: Fri, 10 Feb 2023 18:12:14 +0800 Subject: [PATCH 099/426] [WASM, Kotlin]: Fixed methods and properties in enums (#2919) --- codegen/lib/kotlin_helper.rb | 6 ++ codegen/lib/templates/kotlin/js_enum.erb | 4 +- kotlin/kotlin-js-store/yarn.lock | 103 +------------------ kotlin/wallet-core-kotlin/build.gradle.kts | 5 - wasm/package.json | 4 +- wasm/src/BitcoinSigHashTypeExt.cpp | 29 ++++++ wasm/src/BitcoinSigHashTypeExt.h | 22 ++++ wasm/src/CoinTypeExt.cpp | 97 ++++++++++++++++++ wasm/src/CoinTypeExt.h | 48 +++++++++ wasm/src/CoinTypeExtension.cpp | 114 --------------------- wasm/src/CoinTypeExtension.d.ts | 22 ---- wasm/src/HDVersionExt.cpp | 29 ++++++ wasm/src/HDVersionExt.h | 22 ++++ wasm/src/enum-ext.d.ts | 36 +++++++ wasm/tests/BitcoinSigHashType.test.ts | 30 ++++++ wasm/tests/CoinType.test.ts | 20 ++-- wasm/tests/HDVersion.test.ts | 50 +++++++++ 17 files changed, 383 insertions(+), 258 deletions(-) create mode 100644 wasm/src/BitcoinSigHashTypeExt.cpp create mode 100644 wasm/src/BitcoinSigHashTypeExt.h create mode 100644 wasm/src/CoinTypeExt.cpp create mode 100644 wasm/src/CoinTypeExt.h delete mode 100644 wasm/src/CoinTypeExtension.cpp delete mode 100644 wasm/src/CoinTypeExtension.d.ts create mode 100644 wasm/src/HDVersionExt.cpp create mode 100644 wasm/src/HDVersionExt.h create mode 100644 wasm/src/enum-ext.d.ts create mode 100644 wasm/tests/BitcoinSigHashType.test.ts create mode 100644 wasm/tests/HDVersion.test.ts diff --git a/codegen/lib/kotlin_helper.rb b/codegen/lib/kotlin_helper.rb index bd40ac74d97..0d072ad3637 100644 --- a/codegen/lib/kotlin_helper.rb +++ b/codegen/lib/kotlin_helper.rb @@ -77,6 +77,12 @@ def self.convert_calling_type_js(t) case t.name when :data "#{if t.is_nullable then '?' else '' end}.toUInt8Array()" + when :uint64 + ".toUInt()" + when :int64 + ".toInt()" + when :size + ".toUInt()" else if t.is_enum "#{if t.is_nullable then '?' else '' end}._value" diff --git a/codegen/lib/templates/kotlin/js_enum.erb b/codegen/lib/templates/kotlin/js_enum.erb index d54ffcf6630..4c5895d77ac 100644 --- a/codegen/lib/templates/kotlin/js_enum.erb +++ b/codegen/lib/templates/kotlin/js_enum.erb @@ -12,14 +12,14 @@ actual enum class <%= entity.name %>( <%- entity.properties.each do |property| -%> actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> - get() = <%= KotlinHelper.convert_calling_return_type_js(property.return_type, "_value.#{WasmCppHelper.function_name(entity: entity, function: property)}()") %> + get() = <%= KotlinHelper.convert_calling_return_type_js(property.return_type, "WalletCore.Instance.#{entity.name}Ext.#{WasmCppHelper.function_name(entity: entity, function: property)}(_value)") %> <%- end -%> <%# Method declarations -%> <%- entity.methods.each do |method| -%> <%- next if method.name.start_with?('Delete') -%> actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> = - <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "_value.#{WasmCppHelper.function_name(entity: entity, function: method)}(#{KotlinHelper.calling_parameters_js(method.parameters.drop(1))})") %> + <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "WalletCore.Instance.#{entity.name}Ext.#{WasmCppHelper.function_name(entity: entity, function: method)}(_value#{', ' if not method.parameters.one?}#{KotlinHelper.calling_parameters_js(method.parameters.drop(1))})") %> <%- end -%> <%# Value -%> <% if entity.cases.any? { |e| !e.value.nil? } -%> diff --git a/kotlin/kotlin-js-store/yarn.lock b/kotlin/kotlin-js-store/yarn.lock index 537d261cff0..f2fd767d23d 100644 --- a/kotlin/kotlin-js-store/yarn.lock +++ b/kotlin/kotlin-js-store/yarn.lock @@ -52,71 +52,11 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== - -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== - -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== - dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== - -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== - -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== - "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== -"@trustwallet/wallet-core@3.1.10": - version "3.1.10" - resolved "https://registry.yarnpkg.com/@trustwallet/wallet-core/-/wallet-core-3.1.10.tgz#3af4d0021a91f61faea7d3401d4e04197a843a9b" - integrity sha512-4/Du55HQLcNrJsBXWqUlk+5G7TCan1CfaOhRTxfXad1txWMtTj75kkYnbfST5Oi2DaKbuH86FF+yYroSdgoEiw== - dependencies: - protobufjs ">=6.11.3" - "@types/cookie@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" @@ -160,7 +100,7 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== -"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=13.7.0": +"@types/node@*", "@types/node@>=10.0.0": version "18.11.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== @@ -658,14 +598,6 @@ dom-serialize@^2.2.1: extend "^3.0.0" void-elements "^2.0.0" -dukat@0.5.8-rc.4: - version "0.5.8-rc.4" - resolved "https://registry.yarnpkg.com/dukat/-/dukat-0.5.8-rc.4.tgz#90384dcb50b14c26f0e99dae92b2dea44f5fce21" - integrity sha512-ZnMt6DGBjlVgK2uQamXfd7uP/AxH7RqI0BL9GLrrJb2gKdDxvJChWy+M9AQEaL+7/6TmxzJxFOsRiInY9oGWTA== - dependencies: - google-protobuf "3.12.2" - typescript "3.9.5" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -930,11 +862,6 @@ glob@^7.1.3, glob@^7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -google-protobuf@3.12.2: - version "3.12.2" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.12.2.tgz#50ce9f9b6281235724eb243d6a83e969a2176e53" - integrity sha512-4CZhpuRr1d6HjlyrxoXoocoGFnRYgKULgMtikMddA9ztRyYR59Aondv2FioyxWVamRo0rF2XpYawkTCBEQOSkA== - graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -1231,11 +1158,6 @@ log4js@^6.4.1: rfdc "^1.3.0" streamroller "^3.1.3" -long@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" - integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -1463,24 +1385,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -protobufjs@>=6.11.3: - version "7.2.0" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.0.tgz#ca6b1ceb9a9efe21186ba96178089ec563011a5e" - integrity sha512-hYCqTDuII4iJ4stZqiuGCSU8xxWl5JeXYpwARGtn/tWcKCAro6h3WQz+xpsNbXW0UYqpmTQFEyFWO0G0Kjt64g== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/node" ">=13.7.0" - long "^5.0.0" - punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" @@ -1807,11 +1711,6 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typescript@3.9.5: - version "3.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36" - integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ== - ua-parser-js@^0.7.30: version "0.7.33" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts index 08395c50f08..6ceb9a8cbad 100644 --- a/kotlin/wallet-core-kotlin/build.gradle.kts +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -65,11 +65,6 @@ kotlin { } val jsMain by getting { kotlin.srcDir(projectDir.resolve("src/jsMain/generated")) - - dependencies { - // TODO: Replace with local build - implementation(npm(name = "@trustwallet/wallet-core", version = "3.1.10", generateExternals = false)) - } } } diff --git a/wasm/package.json b/wasm/package.json index eafc3845ed4..996f10ac35e 100644 --- a/wasm/package.json +++ b/wasm/package.json @@ -11,8 +11,8 @@ "codegen:js-browser": "pbjs -t static-module '../src/proto/*.proto' -w closure --no-delimited --force-long -o ../samples/wasm/core_proto.js", "codegen:ts": "pbts -o generated/core_proto.d.ts generated/core_proto.js", "clean": "rm -rf dist generated && mkdir -p dist/generated generated", - "build": "npm run clean && npm run generate && cp -R generated lib dist && tsc && cp src/wallet-core.d.ts dist/src", - "build-and-test": "npm run copy:wasm && npm run build && npm test", + "build": "npm run copy:wasm && npm run clean && npm run generate && cp -R generated lib dist && tsc && cp src/wallet-core.d.ts dist/src", + "build-and-test": "npm run build && npm test", "copy:wasm": "mkdir -p lib && cp ../wasm-build/wasm/wallet-core.* lib", "copy:wasm-sample": "cp ../wasm-build/wasm/wallet-core.* ../samples/wasm/" }, diff --git a/wasm/src/BitcoinSigHashTypeExt.cpp b/wasm/src/BitcoinSigHashTypeExt.cpp new file mode 100644 index 00000000000..c8c1af417cd --- /dev/null +++ b/wasm/src/BitcoinSigHashTypeExt.cpp @@ -0,0 +1,29 @@ +// Copyright © 2017-2023 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 + +#include + +using namespace emscripten; + +#include "BitcoinSigHashTypeExt.h" + +namespace TW::Wasm { + + auto BitcoinSigHashTypeExt::isSingle(TWBitcoinSigHashType type) { + return TWBitcoinSigHashTypeIsSingle(type); + } + auto BitcoinSigHashTypeExt::isNone(TWBitcoinSigHashType type) { + return TWBitcoinSigHashTypeIsNone(type); + } + + EMSCRIPTEN_BINDINGS(Wasm_BitcoinSigHashTypeExt) { + class_("BitcoinSigHashTypeExt") + .class_function("isSingle", &BitcoinSigHashTypeExt::isSingle) + .class_function("isNone", &BitcoinSigHashTypeExt::isNone); + }; +} diff --git a/wasm/src/BitcoinSigHashTypeExt.h b/wasm/src/BitcoinSigHashTypeExt.h new file mode 100644 index 00000000000..3d4f4c1c547 --- /dev/null +++ b/wasm/src/BitcoinSigHashTypeExt.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 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 + +#include + +using namespace emscripten; + +namespace TW::Wasm { + + class BitcoinSigHashTypeExt { + public: + static auto isSingle(TWBitcoinSigHashType type); + static auto isNone(TWBitcoinSigHashType type); + }; +} diff --git a/wasm/src/CoinTypeExt.cpp b/wasm/src/CoinTypeExt.cpp new file mode 100644 index 00000000000..25b63214826 --- /dev/null +++ b/wasm/src/CoinTypeExt.cpp @@ -0,0 +1,97 @@ +// Copyright © 2017-2023 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 +#include "WasmString.h" + +#include "generated/PrivateKey.h" +#include "generated/PublicKey.h" + +#include + +using namespace emscripten; + +#include "CoinTypeExt.h" + +namespace TW::Wasm { + + auto CoinTypeExt::blockchain(TWCoinType coin) { + return TWCoinTypeBlockchain(coin); + } + auto CoinTypeExt::purpose(TWCoinType coin) { + return TWCoinTypePurpose(coin); + } + auto CoinTypeExt::curve(TWCoinType coin) { + return TWCoinTypeCurve(coin); + } + auto CoinTypeExt::xpubVersion(TWCoinType coin) { + return TWCoinTypeXpubVersion(coin); + } + auto CoinTypeExt::xprvVersion(TWCoinType coin) { + return TWCoinTypeXprvVersion(coin); + } + auto CoinTypeExt::hrp(TWCoinType coin) { + return TWCoinTypeHRP(coin); + } + auto CoinTypeExt::p2pkhPrefix(TWCoinType coin) { + return TWCoinTypeP2pkhPrefix(coin); + } + auto CoinTypeExt::p2shPrefix(TWCoinType coin) { + return TWCoinTypeP2shPrefix(coin); + } + auto CoinTypeExt::staticPrefix(TWCoinType coin) { + return TWCoinTypeStaticPrefix(coin); + } + auto CoinTypeExt::chainId(TWCoinType coin) { + return TWStringToStd(TWCoinTypeChainId(coin)); + } + auto CoinTypeExt::slip44Id(TWCoinType coin) { + return TWCoinTypeSlip44Id(coin); + } + auto CoinTypeExt::ss58Prefix(TWCoinType coin) { + return TWCoinTypeSS58Prefix(coin); + } + auto CoinTypeExt::publicKeyType(TWCoinType coin) { + return TWCoinTypePublicKeyType(coin); + } + auto CoinTypeExt::validate(TWCoinType coin, const std::string& address) { + return TWCoinTypeValidate(coin, &address); + } + auto CoinTypeExt::derivationPath(TWCoinType coin) { + return TWStringToStd(TWCoinTypeDerivationPath(coin)); + } + auto CoinTypeExt::derivationPathWithDerivation(TWCoinType coin, TWDerivation derivation) { + return TWStringToStd(TWCoinTypeDerivationPathWithDerivation(coin, derivation)); + } + auto CoinTypeExt::deriveAddress(TWCoinType coin, WasmPrivateKey* privateKey) { + return TWStringToStd(TWCoinTypeDeriveAddress(coin, privateKey->instance)); + } + auto CoinTypeExt::deriveAddressFromPublicKey(TWCoinType coin, WasmPublicKey* publicKey) { + return TWStringToStd(TWCoinTypeDeriveAddressFromPublicKey(coin, publicKey->instance)); + } + + EMSCRIPTEN_BINDINGS(Wasm_CoinTypeExt) { + class_("CoinTypeExt") + .class_function("blockchain", &CoinTypeExt::blockchain) + .class_function("purpose", &CoinTypeExt::purpose) + .class_function("curve", &CoinTypeExt::curve) + .class_function("xpubVersion", &CoinTypeExt::xpubVersion) + .class_function("xprvVersion", &CoinTypeExt::xprvVersion) + .class_function("hrp", &CoinTypeExt::hrp) + .class_function("p2pkhPrefix", &CoinTypeExt::p2pkhPrefix) + .class_function("p2shPrefix", &CoinTypeExt::p2shPrefix) + .class_function("staticPrefix", &CoinTypeExt::staticPrefix) + .class_function("chainId", &CoinTypeExt::chainId) + .class_function("slip44Id", &CoinTypeExt::slip44Id) + .class_function("ss58Prefix", &CoinTypeExt::ss58Prefix) + .class_function("publicKeyType", &CoinTypeExt::publicKeyType) + .class_function("validate", &CoinTypeExt::validate) + .class_function("derivationPath", &CoinTypeExt::derivationPath) + .class_function("derivationPathWithDerivation", &CoinTypeExt::derivationPathWithDerivation) + .class_function("deriveAddress", &CoinTypeExt::deriveAddress, allow_raw_pointers()) + .class_function("deriveAddressFromPublicKey", &CoinTypeExt::deriveAddressFromPublicKey, allow_raw_pointers()); + }; +} diff --git a/wasm/src/CoinTypeExt.h b/wasm/src/CoinTypeExt.h new file mode 100644 index 00000000000..66c62410655 --- /dev/null +++ b/wasm/src/CoinTypeExt.h @@ -0,0 +1,48 @@ +// Copyright © 2017-2023 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 +#include +#include + +#include "generated/PrivateKey.h" +#include "generated/PublicKey.h" + +#include + +using namespace emscripten; + +#include + +#include "WasmString.h" +#include "WasmData.h" + +namespace TW::Wasm { + + class CoinTypeExt { + public: + static auto blockchain(TWCoinType coin); + static auto purpose(TWCoinType coin); + static auto curve(TWCoinType coin); + static auto xpubVersion(TWCoinType coin); + static auto xprvVersion(TWCoinType coin); + static auto hrp(TWCoinType coin); + static auto p2pkhPrefix(TWCoinType coin); + static auto p2shPrefix(TWCoinType coin); + static auto staticPrefix(TWCoinType coin); + static auto chainId(TWCoinType coin); + static auto slip44Id(TWCoinType coin); + static auto ss58Prefix(TWCoinType coin); + static auto publicKeyType(TWCoinType coin); + static auto validate(TWCoinType coin, const std::string& address); + static auto derivationPath(TWCoinType coin); + static auto derivationPathWithDerivation(TWCoinType coin, TWDerivation derivation); + static auto deriveAddress(TWCoinType coin, WasmPrivateKey* privateKey); + static auto deriveAddressFromPublicKey(TWCoinType coin, WasmPublicKey* publicKey); + }; +} diff --git a/wasm/src/CoinTypeExtension.cpp b/wasm/src/CoinTypeExtension.cpp deleted file mode 100644 index e6e1f0a5e27..00000000000 --- a/wasm/src/CoinTypeExtension.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include "WasmString.h" -#include "generated/PrivateKey.h" -#include - -using namespace emscripten; - -namespace TW::Wasm { - class CoinTypeExtension { - public: - TWCoinType mValue; - CoinTypeExtension(TWCoinType value): mValue(value) {} - - auto value() { - return mValue; - } - - auto blockchain() { - return TWCoinTypeBlockchain(mValue); - } - - auto purpose() { - return TWCoinTypePurpose(mValue); - } - - auto curve() { - return TWCoinTypeCurve(mValue); - } - - auto xpubVersion() { - return TWCoinTypeXpubVersion(mValue); - } - - auto xprvVersion() { - return TWCoinTypeXpubVersion(mValue); - } - - auto validate(const std::string& address) { - return TWCoinTypeValidate(mValue, &address); - } - - auto derivationPath() { - return TWStringToStd(TWCoinTypeDerivationPath(mValue)); - } - - auto derivationPathWithDerivation(TWDerivation derivation) { - return TWStringToStd(TWCoinTypeDerivationPathWithDerivation(mValue, derivation)); - } - - auto deriveAddress(WasmPrivateKey* privateKey) { - return TWStringToStd(TWCoinTypeDeriveAddress(mValue, privateKey->instance)); - } - - auto deriveAddressFromPublicKey(WasmPublicKey* publicKey) { - return TWStringToStd(TWCoinTypeDeriveAddressFromPublicKey(mValue, publicKey->instance)); - } - - auto HRP() { - return TWCoinTypeHRP(mValue); - } - - auto P2pkhPrefix() { - return TWCoinTypeP2pkhPrefix(mValue); - } - - auto P2shPrefix() { - return TWCoinTypeP2shPrefix(mValue); - } - - auto staticPrefix() { - return TWCoinTypeStaticPrefix(mValue); - } - - auto chainID() { - return TWStringToStd(TWCoinTypeChainId(mValue)); - } - - auto slip44ID() { - return TWCoinTypeSlip44Id(mValue); - } - - auto SS58Prefix() { - return TWCoinTypeSS58Prefix(mValue); - } - - auto publicKeyType() { - return TWCoinTypePublicKeyType(mValue); - } - }; - - EMSCRIPTEN_BINDINGS(Wasm_CoinTypeExtension) { - class_("CoinTypeExtension") - .constructor() - .function("blockchain", &CoinTypeExtension::blockchain) - .function("purpose", &CoinTypeExtension::purpose) - .function("curve", &CoinTypeExtension::curve) - .function("xpubVersion", &CoinTypeExtension::xpubVersion) - .function("xprvVersion", &CoinTypeExtension::xprvVersion) - .function("validate", &CoinTypeExtension::validate) - .function("derivationPath", &CoinTypeExtension::derivationPath) - .function("derivationPathWithDerivation", &CoinTypeExtension::derivationPathWithDerivation) - .function("deriveAddress", &CoinTypeExtension::deriveAddress, allow_raw_pointers()) - .function("deriveAddressFromPublicKey", &CoinTypeExtension::deriveAddressFromPublicKey, allow_raw_pointers()) - .function("hrp", &CoinTypeExtension::HRP) - .function("P2pkhPrefix", &CoinTypeExtension::P2pkhPrefix) - .function("P2shPrefix", &CoinTypeExtension::P2shPrefix) - .function("staticPrefix", &CoinTypeExtension::staticPrefix) - .function("chainID", &CoinTypeExtension::chainID) - .function("slip44ID", &CoinTypeExtension::slip44ID) - .function("SS58Prefix", &CoinTypeExtension::SS58Prefix) - .function("publicKeyType", &CoinTypeExtension::publicKeyType) - .function("value", &CoinTypeExtension::value); - } -} diff --git a/wasm/src/CoinTypeExtension.d.ts b/wasm/src/CoinTypeExtension.d.ts deleted file mode 100644 index f0169dffe9e..00000000000 --- a/wasm/src/CoinTypeExtension.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -export class CoinTypeExtension { - constructor(CoinType) - value(): CoinType - blockchain(): Blockchain - purpose(): Purpose - curve(): Curve - xpubVersion(): HDVersion - xprvVersion(): HDVersion - validate(address: string): boolean - derivationPath(): string - derivationPathWithDerivation(derivation: Derivation): string - deriveAddress(privateKey: PrivateKey): string - deriveAddressFromPublicKey(publicKey: PublicKey): string - hrp(): HRP - P2pkhPrefix(): number - P2shPrefix(): number - staticPrefix(): number - chainID(): string - slip44ID(): number - SS58Prefix(): number - publicKeyType(): PublicKeyType -} diff --git a/wasm/src/HDVersionExt.cpp b/wasm/src/HDVersionExt.cpp new file mode 100644 index 00000000000..2434ef77f3a --- /dev/null +++ b/wasm/src/HDVersionExt.cpp @@ -0,0 +1,29 @@ +// Copyright © 2017-2023 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 + +#include + +using namespace emscripten; + +#include "HDVersionExt.h" + +namespace TW::Wasm { + + auto HDVersionExt::isPublic(TWHDVersion version) { + return TWHDVersionIsPublic(version); + } + auto HDVersionExt::isPrivate(TWHDVersion version) { + return TWHDVersionIsPrivate(version); + } + + EMSCRIPTEN_BINDINGS(Wasm_HDVersionExt) { + class_("HDVersionExt") + .class_function("isPublic", &HDVersionExt::isPublic) + .class_function("isPrivate", &HDVersionExt::isPrivate); + }; +} diff --git a/wasm/src/HDVersionExt.h b/wasm/src/HDVersionExt.h new file mode 100644 index 00000000000..18d460f68bd --- /dev/null +++ b/wasm/src/HDVersionExt.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 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 + +#include + +using namespace emscripten; + +namespace TW::Wasm { + + class HDVersionExt { + public: + static auto isPublic(TWHDVersion version); + static auto isPrivate(TWHDVersion version); + }; +} diff --git a/wasm/src/enum-ext.d.ts b/wasm/src/enum-ext.d.ts new file mode 100644 index 00000000000..83c302e540e --- /dev/null +++ b/wasm/src/enum-ext.d.ts @@ -0,0 +1,36 @@ +// Copyright © 2017-2023 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. + +export class BitcoinSigHashTypeExt { + static isSingle(type: BitcoinSigHashType): boolean; + static isNone(type: BitcoinSigHashType): boolean; +} + +export class CoinTypeExt { + static blockchain(coin: CoinType): Blockchain; + static purpose(coin: CoinType): Purpose; + static curve(coin: CoinType): Curve; + static xpubVersion(coin: CoinType): HDVersion; + static xprvVersion(coin: CoinType): HDVersion; + static hrp(coin: CoinType): HRP; + static p2pkhPrefix(coin: CoinType): number; + static p2shPrefix(coin: CoinType): number; + static staticPrefix(coin: CoinType): number; + static chainId(coin: CoinType): string; + static slip44Id(coin: CoinType): number; + static ss58Prefix(coin: CoinType): number; + static publicKeyType(coin: CoinType): PublicKeyType; + static validate(coin: CoinType, address: string): boolean; + static derivationPath(coin: CoinType): string; + static derivationPathWithDerivation(coin: CoinType, derivation: Derivation): string; + static deriveAddress(coin: CoinType, privateKey: PrivateKey): string; + static deriveAddressFromPublicKey(coin: CoinType, publicKey: PublicKey): string; +} + +export class HDVersionExt { + static isPublic(version: HDVersion): boolean; + static isPrivate(version: HDVersion): boolean; +} diff --git a/wasm/tests/BitcoinSigHashType.test.ts b/wasm/tests/BitcoinSigHashType.test.ts new file mode 100644 index 00000000000..cfb37e5e2e9 --- /dev/null +++ b/wasm/tests/BitcoinSigHashType.test.ts @@ -0,0 +1,30 @@ +// Copyright © 2017-2023 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. + +import "mocha"; +import { assert } from "chai"; + +describe("BitcoinSigHashType", () => { + it("test isSingle", () => { + const { BitcoinSigHashType, BitcoinSigHashTypeExt } = globalThis.core; + + assert.isFalse(BitcoinSigHashTypeExt.isSingle(BitcoinSigHashType.all)); + assert.isFalse(BitcoinSigHashTypeExt.isSingle(BitcoinSigHashType.none)); + assert.isTrue(BitcoinSigHashTypeExt.isSingle(BitcoinSigHashType.single)); + assert.isFalse(BitcoinSigHashTypeExt.isSingle(BitcoinSigHashType.fork)); + assert.isFalse(BitcoinSigHashTypeExt.isSingle(BitcoinSigHashType.forkBTG)); + }); + + it("test isNone", () => { + const { BitcoinSigHashType, BitcoinSigHashTypeExt } = globalThis.core; + + assert.isFalse(BitcoinSigHashTypeExt.isNone(BitcoinSigHashType.all)); + assert.isTrue(BitcoinSigHashTypeExt.isNone(BitcoinSigHashType.none)); + assert.isFalse(BitcoinSigHashTypeExt.isNone(BitcoinSigHashType.single)); + assert.isFalse(BitcoinSigHashTypeExt.isNone(BitcoinSigHashType.fork)); + assert.isFalse(BitcoinSigHashTypeExt.isNone(BitcoinSigHashType.forkBTG)); + }); +}); diff --git a/wasm/tests/CoinType.test.ts b/wasm/tests/CoinType.test.ts index 416949d944c..44941975f57 100644 --- a/wasm/tests/CoinType.test.ts +++ b/wasm/tests/CoinType.test.ts @@ -1,4 +1,4 @@ -// Copyright © 2017-2022 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,7 +9,7 @@ import { assert } from "chai"; describe("CoinType", () => { it("test raw value", () => { - const { CoinType, CoinTypeExtension, Blockchain, Purpose, Curve, Derivation, PrivateKey, HexCoding } = globalThis.core; + const { CoinType, CoinTypeExt, Blockchain, Purpose, Curve, Derivation, PrivateKey, HexCoding } = globalThis.core; assert.equal(CoinType.bitcoin.value, 0); assert.equal(CoinType.litecoin.value, 2); @@ -18,17 +18,15 @@ describe("CoinType", () => { assert.equal(CoinType.binance.value, 714); assert.equal(CoinType.cosmos.value, 118); assert.equal(CoinType.solana.value, 501); - let val = new CoinTypeExtension(CoinType.solana); - assert.equal(val.blockchain(), Blockchain.solana); - assert.equal(val.value(), CoinType.solana); - assert.equal(val.purpose(), Purpose.bip44); - assert.equal(val.curve(), Curve.ed25519); - assert.isTrue(val.validate("Bxp8yhH9zNwxyE4UqxP7a7hgJ5xTZfxNNft7YJJ2VRjT")) - assert.equal(val.derivationPath(), "m/44'/501'/0'"); - assert.equal(val.derivationPathWithDerivation(Derivation.solanaSolana), "m/44'/501'/0'/0'"); + assert.equal(CoinTypeExt.blockchain(CoinType.solana), Blockchain.solana); + assert.equal(CoinTypeExt.purpose(CoinType.solana), Purpose.bip44); + assert.equal(CoinTypeExt.curve(CoinType.solana), Curve.ed25519); + assert.isTrue(CoinTypeExt.validate(CoinType.solana, "Bxp8yhH9zNwxyE4UqxP7a7hgJ5xTZfxNNft7YJJ2VRjT")) + assert.equal(CoinTypeExt.derivationPath(CoinType.solana), "m/44'/501'/0'"); + assert.equal(CoinTypeExt.derivationPathWithDerivation(CoinType.solana, Derivation.solanaSolana), "m/44'/501'/0'/0'"); let data = HexCoding.decode("8778cc93c6596387e751d2dc693bbd93e434bd233bc5b68a826c56131821cb63") const key = PrivateKey.createWithData(data); - let addr = val.deriveAddress(key); + let addr = CoinTypeExt.deriveAddress(CoinType.solana, key); assert.equal(addr, "7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q") }); }); diff --git a/wasm/tests/HDVersion.test.ts b/wasm/tests/HDVersion.test.ts new file mode 100644 index 00000000000..3ef5aac2729 --- /dev/null +++ b/wasm/tests/HDVersion.test.ts @@ -0,0 +1,50 @@ +// Copyright © 2017-2023 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. + +import "mocha"; +import { assert } from "chai"; + +describe("HDVersion", () => { + it("test isPublic", () => { + const { HDVersion, HDVersionExt } = globalThis.core; + + assert.isFalse(HDVersionExt.isPublic(HDVersion.none)); + assert.isTrue(HDVersionExt.isPublic(HDVersion.xpub)); + assert.isFalse(HDVersionExt.isPublic(HDVersion.xprv)); + assert.isTrue(HDVersionExt.isPublic(HDVersion.ypub)); + assert.isFalse(HDVersionExt.isPublic(HDVersion.yprv)); + assert.isTrue(HDVersionExt.isPublic(HDVersion.zpub)); + assert.isFalse(HDVersionExt.isPublic(HDVersion.zprv)); + assert.isTrue(HDVersionExt.isPublic(HDVersion.ltub)); + assert.isFalse(HDVersionExt.isPublic(HDVersion.ltpv)); + assert.isTrue(HDVersionExt.isPublic(HDVersion.mtub)); + assert.isFalse(HDVersionExt.isPublic(HDVersion.mtpv)); + assert.isTrue(HDVersionExt.isPublic(HDVersion.dpub)); + assert.isFalse(HDVersionExt.isPublic(HDVersion.dprv)); + assert.isTrue(HDVersionExt.isPublic(HDVersion.dgub)); + assert.isFalse(HDVersionExt.isPublic(HDVersion.dgpv)); + }); + + it("test isPrivate", () => { + const { HDVersion, HDVersionExt } = globalThis.core; + + assert.isFalse(HDVersionExt.isPrivate(HDVersion.none)); + assert.isFalse(HDVersionExt.isPrivate(HDVersion.xpub)); + assert.isTrue(HDVersionExt.isPrivate(HDVersion.xprv)); + assert.isFalse(HDVersionExt.isPrivate(HDVersion.ypub)); + assert.isTrue(HDVersionExt.isPrivate(HDVersion.yprv)); + assert.isFalse(HDVersionExt.isPrivate(HDVersion.zpub)); + assert.isTrue(HDVersionExt.isPrivate(HDVersion.zprv)); + assert.isFalse(HDVersionExt.isPrivate(HDVersion.ltub)); + assert.isTrue(HDVersionExt.isPrivate(HDVersion.ltpv)); + assert.isFalse(HDVersionExt.isPrivate(HDVersion.mtub)); + assert.isTrue(HDVersionExt.isPrivate(HDVersion.mtpv)); + assert.isFalse(HDVersionExt.isPrivate(HDVersion.dpub)); + assert.isTrue(HDVersionExt.isPrivate(HDVersion.dprv)); + assert.isFalse(HDVersionExt.isPrivate(HDVersion.dgub)); + assert.isTrue(HDVersionExt.isPrivate(HDVersion.dgpv)); + }); +}); From 06d11f90d9a5f74c8b40a114b1038f66ef4ef00c Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 13 Feb 2023 11:06:52 +0100 Subject: [PATCH 100/426] [Rust]: Hex, Refactor and CI optimizations (#2917) --- .github/workflows/android-ci.yml | 8 +- .github/workflows/ios-ci.yml | 7 + .github/workflows/linux-ci.yml | 7 + CMakeLists.txt | 17 +- android/trustwalletcore/build.gradle | 2 +- kotlin/wallet-core-kotlin/build.gradle.kts | 2 +- rust/Cargo.lock | 174 +++++++++++------- rust/src/encoding/base64.rs | 111 +++++++++++ rust/src/encoding/hex.rs | 99 ++++++++++ rust/src/encoding/mod.rs | 111 +---------- rust/src/memory/mod.rs | 6 + rust/src/starknet/mod.rs | 3 +- samples/cpp/CMakeLists.txt | 11 +- samples/rust/src/build.rs | 1 + src/Aeternity/Address.h | 4 - src/Aion/Address.h | 4 - src/Aion/Entry.cpp | 5 - src/Aion/Entry.h | 1 - src/Algorand/Address.h | 4 - src/Data.h | 15 ++ src/Decred/Signer.cpp | 2 +- src/Everscale/CommonTON/RawAddress.cpp | 1 + src/HexCoding.h | 100 +++++----- src/Icon/Address.cpp | 2 +- src/PrivateKey.h | 7 - src/Tezos/BinaryCoding.cpp | 3 +- tests/chains/Aeternity/AddressTests.cpp | 2 + tests/chains/Aion/AddressTests.cpp | 7 + tests/chains/Binance/SignerTests.cpp | 42 ++--- .../chains/BitcoinGold/TWBitcoinGoldTests.cpp | 2 +- tests/chains/BitcoinGold/TWSignerTests.cpp | 2 +- tests/chains/Cosmos/AddressTests.cpp | 4 +- tests/chains/Decred/SignerTests.cpp | 4 +- tests/chains/Ethereum/AbiTests.cpp | 46 ++--- .../Groestlcoin/TWGroestlcoinSigningTests.cpp | 6 +- tests/chains/IoTeX/SignerTests.cpp | 8 +- tests/chains/Ontology/Oep4Tests.cpp | 14 +- tests/chains/Ontology/ParamsBuilderTests.cpp | 6 +- tests/chains/Tezos/ForgingTests.cpp | 8 +- tests/chains/Tezos/OperationListTests.cpp | 10 +- tests/chains/Tezos/PublicKeyTests.cpp | 2 +- tests/chains/Tezos/SignerTests.cpp | 4 +- .../chains/Zcash/TWZcashTransactionTests.cpp | 6 +- .../Zelcash/TWZelcashTransactionTests.cpp | 6 +- tests/chains/Zilliqa/SignerTests.cpp | 8 +- tests/common/Bech32AddressTests.cpp | 12 +- tools/generate-files | 8 +- tools/rust-bindgen | 45 +++-- 48 files changed, 566 insertions(+), 393 deletions(-) create mode 100644 rust/src/encoding/base64.rs create mode 100644 rust/src/encoding/hex.rs diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index fc7f61fe874..3a58164c7ac 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -41,8 +41,14 @@ jobs: run: tools/install-dependencies if: steps.internal_cache.outputs.cache-hit != 'true' + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + rust + - name: Generate files - run: tools/generate-files + run: tools/generate-files android - name: Build Kotlin doc run: tools/kotlin-doc diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index 4490e8a48dd..3fa46ba4a50 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -27,6 +27,13 @@ jobs: if: steps.internal_cache.outputs.cache-hit != 'true' - name: Run codegen tests run: tools/codegen-test + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + rust + - name: Run iOS tests run: | tools/generate-files diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 84d53924e49..0e290c2e55a 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -28,6 +28,13 @@ jobs: CC: /usr/bin/clang CXX: /usr/bin/clang++ if: steps.internal_cache.outputs.cache-hit != 'true' + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + rust + - name: Code generation run: | tools/generate-files diff --git a/CMakeLists.txt b/CMakeLists.txt index f097a622916..64bb0147fb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,17 +24,20 @@ include(cmake/CompilerWarnings.cmake) include(cmake/StaticAnalyzers.cmake) include(cmake/FindHostPackage.cmake) +set(WALLET_CORE_RS_TARGET_DIR ${CMAKE_SOURCE_DIR}/rust/target) add_library(${PROJECT_NAME}_INTERFACE INTERFACE) target_include_directories(${PROJECT_NAME}_INTERFACE INTERFACE ${PREFIX}/include) target_link_directories(${PROJECT_NAME}_INTERFACE INTERFACE ${PREFIX}/lib) +target_link_directories(${PROJECT_NAME}_INTERFACE INTERFACE ${WALLET_CORE_RS_TARGET_DIR}/release) set_project_warnings(${PROJECT_NAME}_INTERFACE) add_subdirectory(trezor-crypto) set(WALLET_CORE_RS_LIB libwallet_core_rs.a) -set(WALLET_CORE_BINDGEN ${PREFIX}/lib/${WALLET_CORE_RS_LIB}) + +set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/release/${WALLET_CORE_RS_LIB}) if (TW_COMPILE_WASM) message(STATUS "Wasm build enabled") - set(WALLET_CORE_BINDGEN ${PREFIX}/wasm32-unknown-emscripten/release/${WALLET_CORE_RS_LIB}) + set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/wasm32-unknown-emscripten/release/${WALLET_CORE_RS_LIB}) add_subdirectory(wasm) endif () @@ -58,13 +61,13 @@ if (${ANDROID}) add_library(TrustWalletCore SHARED ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) find_library(log-lib log) if (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a") - set(WALLET_CORE_BINDGEN ${PREFIX}/aarch64-linux-android/release/${WALLET_CORE_RS_LIB}) + set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/aarch64-linux-android/release/${WALLET_CORE_RS_LIB}) elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86") - set(WALLET_CORE_BINDGEN ${PREFIX}/i686-linux-android/release/${WALLET_CORE_RS_LIB}) + set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/i686-linux-android/release/${WALLET_CORE_RS_LIB}) elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a") - set(WALLET_CORE_BINDGEN ${PREFIX}/armv7-linux-androideabi/release/${WALLET_CORE_RS_LIB}) + set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/armv7-linux-androideabi/release/${WALLET_CORE_RS_LIB}) elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86_64") - set(WALLET_CORE_BINDGEN ${PREFIX}/x86_64-linux-android/release/${WALLET_CORE_RS_LIB}) + set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/x86_64-linux-android/release/${WALLET_CORE_RS_LIB}) endif() target_link_libraries(TrustWalletCore PUBLIC ${WALLET_CORE_BINDGEN} ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto protobuf ${log-lib} Boost::boost) else () @@ -111,7 +114,7 @@ if (TW_ENABLE_CLANG_TIDY) tw_add_clang_tidy_target(TrustWalletCore) endif () -if (NOT ANDROID AND TW_UNITY_BUILD) +if (TW_UNITY_BUILD) set_target_properties(TrustWalletCore PROPERTIES UNITY_BUILD ON) file(GLOB_RECURSE PROTOBUF_SOURCE_FILES CONFIGURE_DEPENDS src/Cosmos/Protobuf/*.pb.cc src/Hedera/Protobuf/*.pb.cc src/proto/*.pb.cc) diff --git a/android/trustwalletcore/build.gradle b/android/trustwalletcore/build.gradle index bf97bfd2412..b4088f99b48 100644 --- a/android/trustwalletcore/build.gradle +++ b/android/trustwalletcore/build.gradle @@ -11,7 +11,7 @@ android { versionName "1.0" externalNativeBuild { cmake { - arguments "-DCMAKE_BUILD_TYPE=Release" + arguments "-DCMAKE_BUILD_TYPE=Release", "-DTW_UNITY_BUILD=ON" } } } diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts index 6ceb9a8cbad..4af9f55bf0e 100644 --- a/kotlin/wallet-core-kotlin/build.gradle.kts +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -94,7 +94,7 @@ android { externalNativeBuild { cmake { - arguments += listOf("-DCMAKE_BUILD_TYPE=Release", "-DKOTLIN=True") + arguments += listOf("-DCMAKE_BUILD_TYPE=Release", "-DKOTLIN=True", "-DTW_UNITY_BUILD=ON") } } } diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3efb0c41ea4..601d9de59d3 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "ark-ff" @@ -76,9 +76,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "async-trait" -version = "0.1.59" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -145,9 +145,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-slice-cast" @@ -163,9 +163,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cfg-if" @@ -359,6 +359,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hex" version = "0.4.3" @@ -419,17 +425,27 @@ dependencies = [ "syn", ] +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -445,9 +461,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "log" @@ -458,6 +474,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "move-core-types" version = "0.0.4" @@ -473,6 +495,15 @@ dependencies = [ "serde_bytes", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -505,9 +536,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -543,15 +574,15 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "pest" -version = "2.4.1" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" dependencies = [ "thiserror", "ucd-trie", @@ -578,29 +609,28 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -643,18 +673,18 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b15debb4f9d60d767cd8ca9ef7abb2452922f3214671ff052defc7f3502c44" +checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfa8511e9e94fd3de6585a3d3cd00e01ed556dc9814829280af0e8dc72a8f36" +checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" dependencies = [ "proc-macro2", "quote", @@ -699,9 +729,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "semver" @@ -723,27 +753,27 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.7" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -752,9 +782,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -889,9 +919,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -918,18 +948,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -946,19 +976,27 @@ dependencies = [ ] [[package]] -name = "toml" -version = "0.5.9" +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" dependencies = [ - "serde", + "indexmap", + "nom8", + "toml_datetime", ] [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" @@ -980,9 +1018,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-xid" @@ -1017,9 +1055,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1027,9 +1065,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -1042,9 +1080,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1052,9 +1090,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -1065,9 +1103,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wyz" @@ -1086,9 +1124,9 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ "proc-macro2", "quote", diff --git a/rust/src/encoding/base64.rs b/rust/src/encoding/base64.rs new file mode 100644 index 00000000000..8c848129815 --- /dev/null +++ b/rust/src/encoding/base64.rs @@ -0,0 +1,111 @@ +// Copyright © 2017-2023 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. + +use base64::{Engine as _, engine::{general_purpose}}; + +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; +use crate::memory::CByteArray; + +#[no_mangle] +pub extern "C" fn encode_base64(data: *const u8, len: usize, is_url: bool) -> *mut c_char { + let data = unsafe { std::slice::from_raw_parts(data, len) }; + let encoded = if is_url { + general_purpose::URL_SAFE.encode(data) + } else { + general_purpose::STANDARD.encode(data) + }; + CString::new(encoded).unwrap().into_raw() +} + +#[no_mangle] +pub extern "C" fn decode_base64(data: *const c_char, is_url: bool) -> CByteArray { + if data.is_null() { + return CByteArray { data: std::ptr::null_mut(), size: 0 }; + } + let c_str = unsafe { CStr::from_ptr(data) }; + let str_slice = c_str.to_str().unwrap(); + let decoded = if is_url { + general_purpose::URL_SAFE + .decode(str_slice) + } else { + general_purpose::STANDARD + .decode(str_slice) + }; + let decoded = match decoded { + Ok(decoded) => decoded, + Err(_) => return CByteArray { data: std::ptr::null_mut(), size: 0 } + }; + let size = decoded.len(); + let mut decoded_vec = decoded.to_vec(); + let ptr = decoded_vec.as_mut_ptr(); + std::mem::forget(decoded_vec); + CByteArray { data: ptr, size } +} + +#[cfg(test)] +mod tests { + use std::ffi::CString; + use crate::encoding::base64::{decode_base64, encode_base64}; + + #[test] + fn test_encode_base64_ffi() { + let data = b"hello world"; + let encoded = unsafe { + std::ffi::CStr::from_ptr(encode_base64(data.as_ptr(), data.len(), false)) + }; + let expected = "aGVsbG8gd29ybGQ="; + assert_eq!(encoded.to_str().unwrap(), expected); + } + + #[test] + fn test_encode_base64_url_ffi() { + let data = b"+'?ab"; + let encoded = unsafe { + std::ffi::CStr::from_ptr(encode_base64(data.as_ptr(), data.len(), true)) + }; + let expected = "Kyc_YWI="; + assert_eq!(encoded.to_str().unwrap(), expected); + } + + #[test] + fn test_decode_base64_url() { + let encoded = "Kyc_YWI="; + let expected = b"+'?ab"; + + let encoded_c_str = CString::new(encoded).unwrap(); + let encoded_ptr = encoded_c_str.as_ptr(); + + let decoded_ptr = decode_base64(encoded_ptr, true); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + + assert_eq!(decoded_slice, expected); + } + + #[test] + fn test_decode_base64() { + let encoded = "aGVsbG8gd29ybGQh"; + let expected = b"hello world!"; + + let encoded_c_str = CString::new(encoded).unwrap(); + let encoded_ptr = encoded_c_str.as_ptr(); + + let decoded_ptr = decode_base64(encoded_ptr, false); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + + assert_eq!(decoded_slice, expected); + } + + #[test] + fn test_decode_base64_invalid() { + let invalid_encoded = "_This_is_an_invalid_base64_"; + let encoded_c_str = CString::new(invalid_encoded).unwrap(); + let encoded_ptr = encoded_c_str.as_ptr(); + let decoded_ptr = decode_base64(encoded_ptr, false); + assert_eq!(decoded_ptr.data.is_null(), true); + } +} + diff --git a/rust/src/encoding/hex.rs b/rust/src/encoding/hex.rs new file mode 100644 index 00000000000..b1a7869600e --- /dev/null +++ b/rust/src/encoding/hex.rs @@ -0,0 +1,99 @@ +// Copyright © 2017-2023 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. + +use std::ffi::{c_char, CStr, CString}; +use crate::memory::CByteArray; +use hex; + +#[no_mangle] +pub extern "C" fn decode_hex(data: *const c_char) -> CByteArray { + if data.is_null() { + return CByteArray { data: std::ptr::null_mut(), size: 0 }; + } + let c_str = unsafe { CStr::from_ptr(data) }; + let hex_string = c_str.to_str().unwrap(); + let hex_string = if hex_string.starts_with("0x") { &hex_string[2..] } else { hex_string }; + + return match hex::decode(hex_string) { + Ok(mut decoded) => { + let size = decoded.len(); + let ptr = decoded.as_mut_ptr(); + std::mem::forget(decoded); + CByteArray { data: ptr, size } + } + Err(_) => { + CByteArray { data: std::ptr::null_mut(), size: 0 } + } + } +} + +#[no_mangle] +pub extern "C" fn encode_hex(data: *const u8, len: usize, prefixed: bool) -> *mut c_char { + let data = unsafe { std::slice::from_raw_parts(data, len) }; + let mut encoded = hex::encode(data); + if prefixed { + encoded = "0x".to_owned() + &encoded; + } + CString::new(encoded).unwrap().into_raw() +} + +#[cfg(test)] +mod tests { + use std::ffi::CString; + use crate::encoding::hex::{encode_hex, decode_hex}; + + #[test] + fn test_encode_hex_without_prefix() { + let data = b"hello world"; + let encoded = unsafe { + std::ffi::CStr::from_ptr(encode_hex(data.as_ptr(), data.len(), false)) + }; + let expected = "68656c6c6f20776f726c64"; + assert_eq!(encoded.to_str().unwrap(), expected); + } + + #[test] + fn test_encode_hex_with_prefix() { + let data = b"hello world"; + let encoded = unsafe { + std::ffi::CStr::from_ptr(encode_hex(data.as_ptr(), data.len(), true)) + }; + let expected = "0x68656c6c6f20776f726c64"; + assert_eq!(encoded.to_str().unwrap(), expected); + } + + #[test] + fn test_vec_encode() { + let v: Vec = vec![45,181,0,172,145,156,221,227,81,172,54,227,113,29,131,44,109,185,118,105]; + assert_eq!(hex::encode(v), "2db500ac919cdde351ac36e3711d832c6db97669"); + } + + #[test] + fn test_decode_hex() { + let encoded = "7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1"; + + let encoded_c_str = CString::new(encoded).unwrap(); + let encoded_ptr = encoded_c_str.as_ptr(); + + let decoded_ptr = decode_hex(encoded_ptr); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + + assert_eq!(decoded_slice.is_empty(), false); + } + + #[test] + fn test_decode_hex_with_prefix() { + let encoded = "0x7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1"; + + let encoded_c_str = CString::new(encoded).unwrap(); + let encoded_ptr = encoded_c_str.as_ptr(); + + let decoded_ptr = decode_hex(encoded_ptr); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + + assert_eq!(decoded_slice.is_empty(), false); + } +} diff --git a/rust/src/encoding/mod.rs b/rust/src/encoding/mod.rs index 82bff00e40b..b1798fbcce7 100644 --- a/rust/src/encoding/mod.rs +++ b/rust/src/encoding/mod.rs @@ -4,112 +4,5 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use base64::{Engine as _, engine::{general_purpose}}; -use std::ffi::{CStr, CString}; -use std::os::raw::c_char; - -#[no_mangle] -pub extern "C" fn encode_base64(data: *const u8, len: usize, is_url: bool) -> *mut c_char { - let data = unsafe { std::slice::from_raw_parts(data, len) }; - let encoded = if is_url { - general_purpose::URL_SAFE.encode(data) - } else { - general_purpose::STANDARD.encode(data) - }; - CString::new(encoded).unwrap().into_raw() -} - -#[repr(C)] -pub struct CByteArray { - data: *mut u8, - size: usize, -} - -#[no_mangle] -pub extern "C" fn decode_base64(data: *const c_char, is_url: bool) -> CByteArray { - if data.is_null() { - return CByteArray { data: std::ptr::null_mut(), size: 0 }; - } - let c_str = unsafe { CStr::from_ptr(data) }; - let str_slice = c_str.to_str().unwrap(); - let decoded = if is_url { - general_purpose::URL_SAFE - .decode(str_slice) - } else { - general_purpose::STANDARD - .decode(str_slice) - }; - let decoded = match decoded { - Ok(decoded) => decoded, - Err(_) => return CByteArray { data: std::ptr::null_mut(), size: 0 } - }; - let size = decoded.len(); - let mut decoded_vec = decoded.to_vec(); - let ptr = decoded_vec.as_mut_ptr(); - std::mem::forget(decoded_vec); - CByteArray { data: ptr, size } -} - - -#[cfg(test)] -mod tests { - use std::ffi::CString; - use crate::encoding::{decode_base64, encode_base64}; - - #[test] - fn test_encode_base64_ffi() { - let data = b"hello world"; - let encoded = unsafe { - std::ffi::CStr::from_ptr(encode_base64(data.as_ptr(), data.len(), false)) - }; - let expected = "aGVsbG8gd29ybGQ="; - assert_eq!(encoded.to_str().unwrap(), expected); - } - - #[test] - fn test_encode_base64_url_ffi() { - let data = b"+'?ab"; - let encoded = unsafe { - std::ffi::CStr::from_ptr(encode_base64(data.as_ptr(), data.len(), true)) - }; - let expected = "Kyc_YWI="; - assert_eq!(encoded.to_str().unwrap(), expected); - } - - #[test] - fn test_decode_base64_url() { - let encoded = "Kyc_YWI="; - let expected = b"+'?ab"; - - let encoded_c_str = CString::new(encoded).unwrap(); - let encoded_ptr = encoded_c_str.as_ptr(); - - let decoded_ptr = decode_base64(encoded_ptr, true); - let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; - - assert_eq!(decoded_slice, expected); - } - - #[test] - fn test_decode_base64() { - let encoded = "aGVsbG8gd29ybGQh"; - let expected = b"hello world!"; - - let encoded_c_str = CString::new(encoded).unwrap(); - let encoded_ptr = encoded_c_str.as_ptr(); - - let decoded_ptr = decode_base64(encoded_ptr, false); - let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; - - assert_eq!(decoded_slice, expected); - } - - #[test] - fn test_decode_base64_invalid() { - let invalid_encoded = "_This_is_an_invalid_base64_"; - let encoded_c_str = CString::new(invalid_encoded).unwrap(); - let encoded_ptr = encoded_c_str.as_ptr(); - let decoded_ptr = decode_base64(encoded_ptr, false); - assert_eq!(decoded_ptr.data.is_null(), true); - } -} +mod base64; +mod hex; diff --git a/rust/src/memory/mod.rs b/rust/src/memory/mod.rs index 91bb4d4ddf6..a923e03a8c0 100644 --- a/rust/src/memory/mod.rs +++ b/rust/src/memory/mod.rs @@ -6,6 +6,12 @@ use std::ffi::{c_char, CString}; +#[repr(C)] +pub struct CByteArray { + pub data: *mut u8, + pub size: usize, +} + #[no_mangle] pub unsafe extern fn free_string(ptr: *const c_char) { // Take the ownership back to rust and drop the owner diff --git a/rust/src/starknet/mod.rs b/rust/src/starknet/mod.rs index 9da51860024..07d22e32935 100644 --- a/rust/src/starknet/mod.rs +++ b/rust/src/starknet/mod.rs @@ -5,7 +5,8 @@ // file LICENSE at the root of the source code distribution tree. use std::ffi::{c_char, CStr}; -use starknet_crypto::{FieldElement, get_public_key, Signature}; +use starknet_crypto::{get_public_key, Signature}; +use starknet_ff::{FieldElement}; use starknet_signers::{SigningKey, VerifyingKey}; use crate::memory; diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index a7c743f3e81..5fe33b0cd93 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -30,7 +30,7 @@ set (CMAKE_C_STANDARD_REQUIRED ON) # ${WALLET_CORE}/src -- internal TrustWalletCore files, for signer protobuf messages # ${WALLET_CORE}/build/local/include) -- for protobuf includes include_directories (${CMAKE_SOURCE_DIR} ${WALLET_CORE}/include ${WALLET_CORE}/src ${WALLET_CORE}/build/local/include) -link_directories (${WALLET_CORE}/build ${WALLET_CORE}/build/trezor-crypto ${WALLET_CORE}/build/local/lib) +link_directories (${WALLET_CORE}/build ${WALLET_CORE}/build/trezor-crypto ${WALLET_CORE}/build/local/lib ${WALLET_CORE}/rust/target/release) find_library(WALLET_CORE_LIB_FILE TrustWalletCore PATH ${WALLET_CORE}/build) if (NOT WALLET_CORE_LIB_FILE) @@ -39,6 +39,13 @@ else () message ("TrustWalletCore library found here: ${WALLET_CORE_LIB_FILE}") endif () +find_library(WALLET_CORE_RS_LIB_FILE wallet_core_rs PATH ${WALLET_CORE}/rust/target/release) +if (NOT WALLET_CORE_RS_LIB_FILE) + message (FATAL_ERROR "wallet_core_rs library not found. ${SETUP_MESSAGE}") +else () + message ("wallet_core_rs library found here: ${WALLET_CORE_RS_LIB_FILE}") +endif () + # Create all libraries and executables in the root binary dir set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) @@ -71,4 +78,4 @@ SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINK_FLAGS}") add_executable (sample sample.cpp) # link with our library, and default platform libraries -target_link_libraries (sample PUBLIC TrustWalletCore wallet_core_rs TrezorCrypto protobuf pthread ${PLATFORM_LIBS}) +target_link_libraries (sample PUBLIC TrustWalletCore ${WALLET_CORE_RS_LIB_FILE} TrezorCrypto protobuf pthread ${PLATFORM_LIBS}) diff --git a/samples/rust/src/build.rs b/samples/rust/src/build.rs index 4f98962f974..e9d0065dfa9 100644 --- a/samples/rust/src/build.rs +++ b/samples/rust/src/build.rs @@ -34,6 +34,7 @@ fn main() { println!("cargo:rustc-link-search=native={}/build", WALLET_CORE_PROJECT_DIR); println!("cargo:rustc-link-search=native={}/build/trezor-crypto", WALLET_CORE_PROJECT_DIR); println!("cargo:rustc-link-search=native={}/build/local/lib", WALLET_CORE_PROJECT_DIR); + println!("cargo:rustc-link-search=native={}/rust/target/release", WALLET_CORE_PROJECT_DIR); // Libraries; order matters for i in 0..LIBS.len() { diff --git a/src/Aeternity/Address.h b/src/Aeternity/Address.h index 3733a44e93d..eddaea8b804 100644 --- a/src/Aeternity/Address.h +++ b/src/Aeternity/Address.h @@ -34,8 +34,4 @@ class Address { static bool checkPayload(const std::string& payload); }; -inline bool operator==(const Address& lhs, const Address& rhs) { - return lhs.bytes == rhs.bytes; -} - } // namespace TW::Aeternity diff --git a/src/Aion/Address.h b/src/Aion/Address.h index 0a1bc7f4d13..580cf684579 100644 --- a/src/Aion/Address.h +++ b/src/Aion/Address.h @@ -43,8 +43,4 @@ class Address { std::string string() const; }; -inline bool operator==(const Address& lhs, const Address& rhs) { - return lhs.bytes == rhs.bytes; -} - } // namespace TW::Aion diff --git a/src/Aion/Entry.cpp b/src/Aion/Entry.cpp index 454fb6cbc05..98f6bfc0f02 100644 --- a/src/Aion/Entry.cpp +++ b/src/Aion/Entry.cpp @@ -24,11 +24,6 @@ string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& p return Address(publicKey).string(); } -Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { - const auto addr = Address(address); - return {addr.bytes.begin(), addr.bytes.end()}; -} - void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } diff --git a/src/Aion/Entry.h b/src/Aion/Entry.h index b09f3a4871f..4a3944b39a9 100644 --- a/src/Aion/Entry.h +++ b/src/Aion/Entry.h @@ -16,7 +16,6 @@ class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; - Data addressToData(TWCoinType coin, const std::string& address) const; void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; diff --git a/src/Algorand/Address.h b/src/Algorand/Address.h index dc4af6666fe..2928186b9bc 100644 --- a/src/Algorand/Address.h +++ b/src/Algorand/Address.h @@ -37,8 +37,4 @@ class Address { std::string string() const; }; -inline bool operator==(const Address& lhs, const Address& rhs) { - return lhs.bytes == rhs.bytes; -} - } // namespace TW::Algorand diff --git a/src/Data.h b/src/Data.h index 6ab74ccaba9..4abfed23abd 100644 --- a/src/Data.h +++ b/src/Data.h @@ -20,6 +20,21 @@ inline void pad_left(Data& data, const uint32_t size) { data.insert(data.begin(), size - data.size(), 0); } +template +inline Data data(It&& begin, It&& end) { + return Data(begin, end); +} + +template +inline Data data_from(const Collection& collection) { + Data out; + out.reserve(collection.size()); + for (auto&& cur : collection) { + out.emplace_back(uint8_t(cur)); + } + return out; +} + inline Data data(const std::string& data) { return Data(data.begin(), data.end()); } diff --git a/src/Decred/Signer.cpp b/src/Decred/Signer.cpp index 5cb610c137e..5cae54cb126 100644 --- a/src/Decred/Signer.cpp +++ b/src/Decred/Signer.cpp @@ -193,7 +193,7 @@ Data Signer::keyForPublicKeyHash(const Data& hash) const { } Data Signer::scriptForScriptHash(const Data& hash) const { - auto hashString = hex(hash.begin(), hash.end()); + auto hashString = hex(hash); auto it = input.scripts().find(hashString); if (it == input.scripts().end()) { // Error: Missing redeem script diff --git a/src/Everscale/CommonTON/RawAddress.cpp b/src/Everscale/CommonTON/RawAddress.cpp index 5aa6199062f..326737c486c 100644 --- a/src/Everscale/CommonTON/RawAddress.cpp +++ b/src/Everscale/CommonTON/RawAddress.cpp @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include #include "RawAddress.h" #include "HexCoding.h" diff --git a/src/HexCoding.h b/src/HexCoding.h index f047065a85b..48d1d18661b 100644 --- a/src/HexCoding.h +++ b/src/HexCoding.h @@ -7,14 +7,34 @@ #pragma once #include "Data.h" +#include "rust/bindgen/WalletCoreRSBindgen.h" -#include - +#include #include #include #include #include + +namespace TW::internal { +/// Parses a string of hexadecimal values. +/// +/// \returns the array or parsed bytes or an empty array if the string is not +/// valid hexadecimal. +inline Data parse_hex(const std::string& input) { + if (input.empty()) { + return Data(); + } + auto decoded = decode_hex(input.c_str()); + if (decoded.data == nullptr || decoded.size == 0) { + return Data(); + } + std::vector decoded_vec(&decoded.data[0], &decoded.data[decoded.size]); + std::free(decoded.data); + return decoded_vec; +} +} + namespace TW { inline bool is_hex_encoded(const std::string& s) @@ -26,65 +46,39 @@ inline bool is_hex_encoded(const std::string& s) return with_0x || without_0x; } -std::tuple value(uint8_t c); - -/// Converts a range of bytes to a hexadecimal string representation. -template -inline std::string hex(const Iter begin, const Iter end) { - static constexpr std::array hexmap = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - - std::string result; - result.reserve((end - begin) * 2); - - for (auto it = begin; it < end; ++it) { - auto val = static_cast(*it); - result.push_back(hexmap[val >> 4]); - result.push_back(hexmap[val & 0x0f]); - } - - return result; -} - /// Converts a collection of bytes to a hexadecimal string representation. template -inline std::string hex(const T& collection) { - return hex(std::begin(collection), std::end(collection)); +inline std::string hex(const T& collection, bool prefixed = false) { + auto rust_functor = [prefixed](auto&& collection){ + auto res = encode_hex(collection.data(), collection.size(), prefixed); + std::string encoded_str(res); + free_string(res); + return encoded_str; + }; + if constexpr (std::is_same_v) { + return rust_functor(collection); + } + else if constexpr (std::is_same_v) { + return rust_functor(data(collection)); + } + else { + return rust_functor(data_from(collection)); + } } /// same as hex, with 0x prefix template -inline std::string hexEncoded(const T& collection) { - return hex(std::begin(collection), std::end(collection)).insert(0, "0x"); +inline std::string hexEncoded(T&& collection) { + return hex(std::forward(collection), true); } /// Converts a `uint64_t` value to a hexadecimal string. inline std::string hex(uint64_t value) { - auto bytes = reinterpret_cast(&value); - return hex(std::reverse_iterator(bytes + sizeof(value)), - std::reverse_iterator(bytes)); -} - -/// Parses a string of hexadecimal values. -/// -/// \returns the array or parsed bytes or an empty array if the string is not -/// valid hexadecimal. -template -inline Data parse_hex(const Iter begin, const Iter end) { - auto it = begin; - - // Skip `0x` - if (end - begin >= 2 && *begin == '0' && *(begin + 1) == 'x') { - it += 2; - } - try { - std::string temp; - boost::algorithm::unhex(it, end, std::back_inserter(temp)); - return Data(temp.begin(), temp.end()); - } catch (...) { - return {}; - } + const uint8_t* begin = reinterpret_cast(&value); + const uint8_t* end = begin + sizeof(value); + Data v(begin, end); + std::reverse(v.begin(), v.end()); + return hex(v); } /// Parses a string of hexadecimal values. @@ -98,9 +92,9 @@ inline Data parse_hex(const std::string& string, bool padLeft = false) { temp.erase(0, 2); } temp.insert(0, 1, '0'); - return parse_hex(temp.begin(), temp.end()); + return internal::parse_hex(temp); } - return parse_hex(string.begin(), string.end()); + return internal::parse_hex(string); } inline const char* hex_char_to_bin(char c) { diff --git a/src/Icon/Address.cpp b/src/Icon/Address.cpp index 79a680098d2..b8f799f48a6 100644 --- a/src/Icon/Address.cpp +++ b/src/Icon/Address.cpp @@ -39,7 +39,7 @@ Address::Address(const std::string& string) { throw std::invalid_argument("Invalid address prefix"); } - const auto data = parse_hex(string.begin() + 2, string.end()); + const auto data = parse_hex(string.substr(2)); std::copy(data.begin(), data.end(), bytes.begin()); } diff --git a/src/PrivateKey.h b/src/PrivateKey.h index 32b2877a0ff..50b16b188d0 100644 --- a/src/PrivateKey.h +++ b/src/PrivateKey.h @@ -87,13 +87,6 @@ class PrivateKey { void cleanup(); }; -inline bool operator==(const PrivateKey& lhs, const PrivateKey& rhs) { - return lhs.bytes == rhs.bytes; -} -inline bool operator!=(const PrivateKey& lhs, const PrivateKey& rhs) { - return lhs.bytes != rhs.bytes; -} - } // namespace TW /// Wrapper for C interface. diff --git a/src/Tezos/BinaryCoding.cpp b/src/Tezos/BinaryCoding.cpp index 1f030be4cfd..363ab9ef76b 100644 --- a/src/Tezos/BinaryCoding.cpp +++ b/src/Tezos/BinaryCoding.cpp @@ -20,7 +20,8 @@ std::string base58ToHex(const std::string& string, size_t prefixLength) { if (decoded.size() < prefixLength) { return ""; } - return TW::hex(decoded.data() + prefixLength, decoded.data() + decoded.size()); + Data v(decoded.data() + prefixLength, decoded.data() + decoded.size()); + return TW::hex(v); } PublicKey parsePublicKey(const std::string& publicKey) { diff --git a/tests/chains/Aeternity/AddressTests.cpp b/tests/chains/Aeternity/AddressTests.cpp index c08866c05e7..753e0b39204 100644 --- a/tests/chains/Aeternity/AddressTests.cpp +++ b/tests/chains/Aeternity/AddressTests.cpp @@ -14,11 +14,13 @@ TEST(AeternityAddress, FromPublicKey) { auto publicKey = PublicKey(parse_hex("ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148"), TWPublicKeyTypeED25519); auto address = Address(publicKey); ASSERT_EQ(address.string(), "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); + ASSERT_ANY_THROW(Address(PublicKey(parse_hex("03df9a5e4089f89d45913fb2b856de984c7e8bf1344cc6444cc9705899a48c939d"), TWPublicKeyTypeSECP256k1))); } TEST(AeternityAddress, FromString) { auto address = Address("ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); ASSERT_EQ(address.string(), "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); + ASSERT_ANY_THROW(Address("invalid")); } } // namespace TW::Aeternity::tests diff --git a/tests/chains/Aion/AddressTests.cpp b/tests/chains/Aion/AddressTests.cpp index 05f37edb64e..d773b092f4d 100644 --- a/tests/chains/Aion/AddressTests.cpp +++ b/tests/chains/Aion/AddressTests.cpp @@ -23,6 +23,13 @@ TEST(AionAddress, FromString) { std::string aionAddress = "0xa0d2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"; const auto address = Address(aionAddress); ASSERT_EQ(address.string(), aionAddress); + ASSERT_ANY_THROW(Address("0xffff")); +} + +TEST(AionAddress, InvalidFromData) { + ASSERT_ANY_THROW(Address(parse_hex("0xffff"))); + auto aionAddress = parse_hex("0xa0d2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"); + [[maybe_unused]] auto res = Address(aionAddress); } TEST(AionAddress, isValid) { diff --git a/tests/chains/Binance/SignerTests.cpp b/tests/chains/Binance/SignerTests.cpp index 66e5d88fc91..cba2b1a48a5 100644 --- a/tests/chains/Binance/SignerTests.cpp +++ b/tests/chains/Binance/SignerTests.cpp @@ -44,7 +44,7 @@ TEST(BinanceSigner, Sign) { auto signer = Binance::Signer(std::move(input)); auto signature = signer.sign(); - ASSERT_EQ(hex(signature.begin(), signature.end()), + ASSERT_EQ(hex(signature), "9123cb6906bb20aeb753f4a121d4d88ff0e9750ba75b0c4e10d76caee1e7d2481290fa3b9887a6225d69" "97f5f939ef834ea61d596a314237c48e560da9e17b5a"); } @@ -73,7 +73,7 @@ TEST(BinanceSigner, Build) { auto signer = Binance::Signer(std::move(input)); auto result = signer.build(); - ASSERT_EQ(hex(result.begin(), result.end()), "db01" + ASSERT_EQ(hex(result), "db01" "f0625dee" "0a65" "ce6dc043" @@ -128,13 +128,13 @@ TEST(BinanceSigner, BuildSend) { auto signer = Binance::Signer(std::move(signingInput)); auto signature = signer.sign(); - ASSERT_EQ(hex(signature.begin(), signature.end()), + ASSERT_EQ(hex(signature), "c65a13440f18a155bd971ee40b9e0dd58586f5bf344e12ec4c76c439aebca8c7789bab7bfbfb4ce89aad" "c4a02df225b6b6efc861c13bbeb5f7a3eea2d7ffc80f"); auto result = signer.build(); - ASSERT_EQ(hex(result.begin(), result.end()), "cc01" + ASSERT_EQ(hex(result), "cc01" "f0625dee" "0a4e" "2a2c87fa" @@ -190,7 +190,7 @@ TEST(BinanceSigner, BuildSend2) { *signingInput.mutable_send_order() = sendOrder; const auto data = Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "c601" "f0625dee" "0a52" @@ -243,7 +243,7 @@ TEST(BinanceSigner, BuildHTLT) { htltOrder.set_cross_chain(false); const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "ee01f0625dee0a7ab33f9a240a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e112140153f11d6db7" "e69c7d51e771c697378018fb6c242a20e8eae926261ab77d018202434791a335249b470246a7b02e28c3" "b2fb6ffad8f330e1d1c7eb053a0a0a03424e421080c2d72f42113130303030303030303a4254432d3144" @@ -276,7 +276,7 @@ TEST(BinanceSigner, BuildDepositHTLT) { *depositHTLTOrder.add_amount() = token; const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "c001f0625dee0a4c639864960a140153f11d6db7e69c7d51e771c697378018fb6c24120e0a074254432d" "3144431080c2d72f1a20dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5" "126c0a26eb5ae98721038df6960084e20b2d07d50e1422f94105c6241d9f1482a4eb79ce8bfd460f19e4" @@ -307,7 +307,7 @@ TEST(BinanceSigner, BuildClaimHTLT) { const auto data = Binance::Signer(std::move(signingInput)).build(); ASSERT_EQ( - hex(data.begin(), data.end()), + hex(data), "d401f0625dee0a5ec16653000a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11220dd8fd4719741844d35" "eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e51a20bda6933c7757d0ca428aa01fb9d0935a231f87bf" "2deeb9b409cea3f2d580a2cc126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561a" @@ -334,7 +334,7 @@ TEST(BinanceSigner, BuildRefundHTLT) { refundHTLTOrder.set_swap_id(swapID.data(), swapID.size()); const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "b201f0625dee0a3c3454a27c0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11220dd8fd4719741" "844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5126e0a26eb5ae9872103a9a55c040c8e" "b8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240c9f36142534d16ec8ce656f8eb73" @@ -362,7 +362,7 @@ TEST(BinanceSigner, BuildIssueOrder) { issueOrder.set_mintable(true); const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "b601f0625dee0a40" "17efab80" "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120f" @@ -392,7 +392,7 @@ TEST(BinanceSigner, BuildMintOrder) { mintOrder.set_amount(1000000); const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "a101f0625dee0a2b" "467e0829" "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b" @@ -420,7 +420,7 @@ TEST(BinanceSigner, BuildBurnOrder) { burnOrder.set_amount(1000000); const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "a101f0625dee0a2b" "7ed2d2a0" "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b" @@ -448,7 +448,7 @@ TEST(BinanceSigner, BuildFreezeOrder) { freezeOrder.set_amount(1000000); const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "a101f0625dee0a2b" "e774b32d" "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b" @@ -476,7 +476,7 @@ TEST(BinanceSigner, BuildUnfreezeOrder) { unfreezeOrder.set_amount(1000000); const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "a101f0625dee0a2b" "6515ff0d" "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b" @@ -511,7 +511,7 @@ TEST(BinanceSigner, BuildTransferOutOrder) { token.set_amount(100000000); const auto data = Binance::Signer(std::move(input)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "b701f0625dee0a41800819c00a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1121435552c16704d" "214347f29fa77f77da6d75d7c7521a0a0a03424e421080c2d72f20cec2f105126e0a26eb5ae9872103a9" "a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc712407eda148e1167b1be12" @@ -543,7 +543,7 @@ TEST(BinanceSigner, BuildSideChainDelegate) { token.set_amount(200000000); const auto data = Binance::Signer(std::move(input)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "ba01f0625dee0a44e3a07fd20a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e112147cc24a1de524" "5f14a95e457f903bcc8461ac869c1a0a0a03424e42108084af5f220663686170656c126e0a26eb5ae987" "2103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc7124039302c9975fb" @@ -578,7 +578,7 @@ TEST(BinanceSigner, BuildSideChainRedelegate) { token.set_amount(100000000); const auto data = Binance::Signer(std::move(input)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "d001f0625dee0a5ae3ced3640a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11214ce2e3593c138" "d51c38c7447227605d2f444a881e1a140fa0ad646c86d9171e36a68ba47fbc5e0b0dd078220a0a03424e" "421080c2d72f2a0663686170656c126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08" @@ -611,7 +611,7 @@ TEST(BinanceSigner, BuildSideChainUndelegate) { token.set_amount(100000000); const auto data = Binance::Signer(std::move(input)).build(); - ASSERT_EQ(hex(data.begin(), data.end()), + ASSERT_EQ(hex(data), "ba01f0625dee0a44514f7e0e0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11214ce2e3593c138" "d51c38c7447227605d2f444a881e1a0a0a03424e421080c2d72f220663686170656c126e0a26eb5ae987" "2103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240a622b7ca7a28" @@ -641,7 +641,7 @@ TEST(BinanceSigner, BuildTimeLockOrder) { lockOrder.set_lock_time(1600001371); const auto data = Binance::Signer(std::move(signingInput)).build(); - EXPECT_EQ(hex(data.begin(), data.end()), + EXPECT_EQ(hex(data), "bf01f0625dee0a49" "07921531" "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1121c4465736372697074696f6e206c6f636b656420666f72206f666665721a090a03424e4210c0843d20dbaaf8fa05126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240c270822b9515ba486c6a6b3472d388a5aea872ed960c0b53de0fafdc8682ef473a126f01e7dd2c00f04a0138a601b9540f54b14026846de362f7ab7f9fed948b180f2001" @@ -670,7 +670,7 @@ TEST(BinanceSigner, BuildTimeRelockOrder) { relockOrder.set_lock_time(1600001371); const auto data = Binance::Signer(std::move(signingInput)).build(); - EXPECT_EQ(hex(data.begin(), data.end()), + EXPECT_EQ(hex(data), "c201f0625dee0a4c504711da0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e110cd021a1c446573" "6372697074696f6e206c6f636b656420666f72206f6666657222090a03424e4210c0843d28dbaaf8fa05" "126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc7" @@ -695,7 +695,7 @@ TEST(BinanceSigner, BuildTimeUnlockOrder) { unlockOrder.set_id(333); const auto data = Binance::Signer(std::move(signingInput)).build(); - EXPECT_EQ(hex(data.begin(), data.end()), + EXPECT_EQ(hex(data), "9301f0625dee0a1dc4050c6c0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e110cd02126e0a26eb" "5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240da777b" "fd2032834f59ec9fe69fd6eaa4aca24242dfbc5ec4ef8c435cb9da7eb05ab78e1b8ca9f109657cb77996" diff --git a/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp b/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp index 760cd79b9a6..d0285c3b2c9 100644 --- a/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp +++ b/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp @@ -86,7 +86,7 @@ TEST(TWBitcoinGoldTxGeneration, TxGeneration) { auto scriptPub1 = Script(parse_hex("0014db746a75d9aae8995d135b1e19a04d7765242a8f")); auto scriptHash = std::vector(); scriptPub1.matchPayToWitnessPublicKeyHash(scriptHash); - auto scriptHashHex = hex(scriptHash.begin(), scriptHash.end()); + auto scriptHashHex = hex(scriptHash); auto redeemScript = Script::buildPayToPublicKeyHash(scriptHash); auto scriptString = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); diff --git a/tests/chains/BitcoinGold/TWSignerTests.cpp b/tests/chains/BitcoinGold/TWSignerTests.cpp index 673e6437912..ad8497ffdf6 100644 --- a/tests/chains/BitcoinGold/TWSignerTests.cpp +++ b/tests/chains/BitcoinGold/TWSignerTests.cpp @@ -40,7 +40,7 @@ TEST(TWBitcoinGoldSigner, SignTransaction) { auto scriptPub1 = Script(parse_hex("0014db746a75d9aae8995d135b1e19a04d7765242a8f")); auto scriptHash = std::vector(); scriptPub1.matchPayToWitnessPublicKeyHash(scriptHash); - auto scriptHashHex = hex(scriptHash.begin(), scriptHash.end()); + auto scriptHashHex = hex(scriptHash); auto redeemScript = Script::buildPayToPublicKeyHash(scriptHash); auto scriptString = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); diff --git a/tests/chains/Cosmos/AddressTests.cpp b/tests/chains/Cosmos/AddressTests.cpp index bcb2012b58e..a50d7596730 100644 --- a/tests/chains/Cosmos/AddressTests.cpp +++ b/tests/chains/Cosmos/AddressTests.cpp @@ -25,7 +25,7 @@ TEST(CosmosAddress, Invalid) { TEST(CosmosAddress, Cosmos_FromPublicKey) { auto privateKey = PrivateKey(parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005")); auto publicKeyData = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - ASSERT_EQ(hex(publicKeyData.bytes.begin(), publicKeyData.bytes.end()), "0257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc5"); + ASSERT_EQ(hex(publicKeyData.bytes), "0257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc5"); auto publicKey = PublicKey(publicKeyData); auto address = Address("cosmos", publicKey); @@ -56,7 +56,7 @@ TEST(CosmosAddress, Cosmos_Invalid) { TEST(CosmosAddress, ThorFromPublicKey) { auto privateKey = PrivateKey(parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e")); auto publicKeyData = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - ASSERT_EQ(hex(publicKeyData.bytes.begin(), publicKeyData.bytes.end()), "03ed997e396cf4292f5fce5a42bba41599ccd5d96e313154a7c9ea7049de317c77"); + ASSERT_EQ(hex(publicKeyData.bytes), "03ed997e396cf4292f5fce5a42bba41599ccd5d96e313154a7c9ea7049de317c77"); auto publicKey = PublicKey(publicKeyData); auto address = Address("thor", publicKey); diff --git a/tests/chains/Decred/SignerTests.cpp b/tests/chains/Decred/SignerTests.cpp index 85cf02ea128..94e6a3f9b73 100644 --- a/tests/chains/Decred/SignerTests.cpp +++ b/tests/chains/Decred/SignerTests.cpp @@ -164,7 +164,7 @@ TEST(DecredSigner, SignP2SH) { auto redeemScript = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); auto scriptHash = Hash::ripemd(Hash::sha256(redeemScript.bytes)); auto scriptString = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); - (*input.mutable_scripts())[hex(scriptHash.begin(), scriptHash.end())] = scriptString; + (*input.mutable_scripts())[hex(scriptHash)] = scriptString; auto utxo0 = input.add_utxo(); auto utxo0Script = Bitcoin::Script::buildPayToScriptHash(scriptHash); @@ -401,7 +401,7 @@ TEST(DecredSigning, SignP2WPKH_NegativeAddressWrongType) { auto scriptPub1 = Bitcoin::Script(parse_hex("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); auto scriptHash = std::vector(); scriptPub1.matchPayToWitnessPublicKeyHash(scriptHash); - auto scriptHashHex = hex(scriptHash.begin(), scriptHash.end()); + auto scriptHashHex = hex(scriptHash); ASSERT_EQ(scriptHashHex, "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1"); auto redeemScript = Bitcoin::Script::buildPayToPublicKeyHash(scriptHash); diff --git a/tests/chains/Ethereum/AbiTests.cpp b/tests/chains/Ethereum/AbiTests.cpp index 3575dcb0f95..e23289a6446 100644 --- a/tests/chains/Ethereum/AbiTests.cpp +++ b/tests/chains/Ethereum/AbiTests.cpp @@ -1003,9 +1003,9 @@ TEST(EthereumAbi, EncodeSignature) { func.encode(encoded); EXPECT_EQ(encoded.size(), 32 * 2 + 4ul); - EXPECT_EQ(hex(encoded.begin(), encoded.begin() + 4), "72ed38b6"); - EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36), "0000000000000000000000000000000000000000000000000000000000000045"); - EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68), "0000000000000000000000000000000000000000000000000000000000000001"); + EXPECT_EQ(hex(data(encoded.begin(), encoded.begin() + 4)), "72ed38b6"); + EXPECT_EQ(hex(data(encoded.begin() + 4, encoded.begin() + 36)), "0000000000000000000000000000000000000000000000000000000000000045"); + EXPECT_EQ(hex(data(encoded.begin() + 36, encoded.begin() + 68)), "0000000000000000000000000000000000000000000000000000000000000001"); } TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase1) { @@ -1025,16 +1025,16 @@ TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase1) { func.encode(encoded); EXPECT_EQ(encoded.size(), 32 * 9 + 4ul); - EXPECT_EQ(hex(encoded.begin() + 0, encoded.begin() + 4), "a5643bf2"); - EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36), "0000000000000000000000000000000000000000000000000000000000000060"); - EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68), "0000000000000000000000000000000000000000000000000000000000000001"); - EXPECT_EQ(hex(encoded.begin() + 68, encoded.begin() + 100), "00000000000000000000000000000000000000000000000000000000000000a0"); - EXPECT_EQ(hex(encoded.begin() + 100, encoded.begin() + 132), "0000000000000000000000000000000000000000000000000000000000000004"); - EXPECT_EQ(hex(encoded.begin() + 132, encoded.begin() + 164), "6461766500000000000000000000000000000000000000000000000000000000"); - EXPECT_EQ(hex(encoded.begin() + 164, encoded.begin() + 196), "0000000000000000000000000000000000000000000000000000000000000003"); - EXPECT_EQ(hex(encoded.begin() + 196, encoded.begin() + 228), "0000000000000000000000000000000000000000000000000000000000000001"); - EXPECT_EQ(hex(encoded.begin() + 228, encoded.begin() + 260), "0000000000000000000000000000000000000000000000000000000000000002"); - EXPECT_EQ(hex(encoded.begin() + 260, encoded.begin() + 292), "0000000000000000000000000000000000000000000000000000000000000003"); + EXPECT_EQ(hex(data(encoded.begin() + 0, encoded.begin() + 4)), "a5643bf2"); + EXPECT_EQ(hex(data(encoded.begin() + 4, encoded.begin() + 36)), "0000000000000000000000000000000000000000000000000000000000000060"); + EXPECT_EQ(hex(data(encoded.begin() + 36, encoded.begin() + 68)), "0000000000000000000000000000000000000000000000000000000000000001"); + EXPECT_EQ(hex(data(encoded.begin() + 68, encoded.begin() + 100)), "00000000000000000000000000000000000000000000000000000000000000a0"); + EXPECT_EQ(hex(data(encoded.begin() + 100, encoded.begin() + 132)), "0000000000000000000000000000000000000000000000000000000000000004"); + EXPECT_EQ(hex(data(encoded.begin() + 132, encoded.begin() + 164)), "6461766500000000000000000000000000000000000000000000000000000000"); + EXPECT_EQ(hex(data(encoded.begin() + 164, encoded.begin() + 196)), "0000000000000000000000000000000000000000000000000000000000000003"); + EXPECT_EQ(hex(data(encoded.begin() + 196, encoded.begin() + 228)), "0000000000000000000000000000000000000000000000000000000000000001"); + EXPECT_EQ(hex(data(encoded.begin() + 228, encoded.begin() + 260)), "0000000000000000000000000000000000000000000000000000000000000002"); + EXPECT_EQ(hex(data(encoded.begin() + 260, encoded.begin() + 292)), "0000000000000000000000000000000000000000000000000000000000000003"); } TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase2) { @@ -1053,16 +1053,16 @@ TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase2) { func.encode(encoded); EXPECT_EQ(encoded.size(), 32 * 9 + 4ul); - EXPECT_EQ(hex(encoded.begin() + 0, encoded.begin() + 4), "47b941bf"); - EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36), "0000000000000000000000000000000000000000000000000000000000000123"); - EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68), "0000000000000000000000000000000000000000000000000000000000000080"); - EXPECT_EQ(hex(encoded.begin() + 68, encoded.begin() + 100), "3132333435363738393000000000000000000000000000000000000000000000"); - EXPECT_EQ(hex(encoded.begin() + 100, encoded.begin() + 132), "00000000000000000000000000000000000000000000000000000000000000e0"); - EXPECT_EQ(hex(encoded.begin() + 132, encoded.begin() + 164), "0000000000000000000000000000000000000000000000000000000000000002"); - EXPECT_EQ(hex(encoded.begin() + 164, encoded.begin() + 196), "0000000000000000000000000000000000000000000000000000000000000456"); - EXPECT_EQ(hex(encoded.begin() + 196, encoded.begin() + 228), "0000000000000000000000000000000000000000000000000000000000000789"); - EXPECT_EQ(hex(encoded.begin() + 228, encoded.begin() + 260), "000000000000000000000000000000000000000000000000000000000000000d"); - EXPECT_EQ(hex(encoded.begin() + 260, encoded.begin() + 292), "48656c6c6f2c20776f726c642100000000000000000000000000000000000000"); + EXPECT_EQ(hex(data(encoded.begin() + 0, encoded.begin() + 4)), "47b941bf"); + EXPECT_EQ(hex(data(encoded.begin() + 4, encoded.begin() + 36)), "0000000000000000000000000000000000000000000000000000000000000123"); + EXPECT_EQ(hex(data(encoded.begin() + 36, encoded.begin() + 68)), "0000000000000000000000000000000000000000000000000000000000000080"); + EXPECT_EQ(hex(data(encoded.begin() + 68, encoded.begin() + 100)), "3132333435363738393000000000000000000000000000000000000000000000"); + EXPECT_EQ(hex(data(encoded.begin() + 100, encoded.begin() + 132)), "00000000000000000000000000000000000000000000000000000000000000e0"); + EXPECT_EQ(hex(data(encoded.begin() + 132, encoded.begin() + 164)), "0000000000000000000000000000000000000000000000000000000000000002"); + EXPECT_EQ(hex(data(encoded.begin() + 164, encoded.begin() + 196)), "0000000000000000000000000000000000000000000000000000000000000456"); + EXPECT_EQ(hex(data(encoded.begin() + 196, encoded.begin() + 228)), "0000000000000000000000000000000000000000000000000000000000000789"); + EXPECT_EQ(hex(data(encoded.begin() + 228, encoded.begin() + 260)), "000000000000000000000000000000000000000000000000000000000000000d"); + EXPECT_EQ(hex(data(encoded.begin() + 260, encoded.begin() + 292)), "48656c6c6f2c20776f726c642100000000000000000000000000000000000000"); } TEST(EthereumAbi, DecodeFunctionOutputCase1) { diff --git a/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp index 4134ab277f7..6593cb41c1d 100644 --- a/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp +++ b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp @@ -120,14 +120,14 @@ TEST(GroestlcoinSigning, SignP2SH_P2WPKH) { auto utxoKey0 = PrivateKey(parse_hex("302fc195a8fc96c5a581471e67e4c1ac2efda252f76ad5c77a53764c70d58f91")); auto pubKey0 = utxoKey0.getPublicKey(TWPublicKeyTypeSECP256k1); auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(pubKey0.bytes)); - EXPECT_EQ(hex(utxoPubkeyHash.begin(), utxoPubkeyHash.end()), "2fc7d70acef142d1f7b5ef2f20b1a9b759797674"); + EXPECT_EQ(hex(utxoPubkeyHash), "2fc7d70acef142d1f7b5ef2f20b1a9b759797674"); input.add_private_key(utxoKey0.bytes.data(), utxoKey0.bytes.size()); auto redeemScript = Script::buildPayToWitnessPublicKeyHash(utxoPubkeyHash); auto scriptHash = Hash::ripemd(Hash::sha256(redeemScript.bytes)); - ASSERT_EQ(hex(scriptHash.begin(), scriptHash.end()), "0055b0c94df477ee6b9f75185dfc9aa8ce2e52e4"); + ASSERT_EQ(hex(scriptHash), "0055b0c94df477ee6b9f75185dfc9aa8ce2e52e4"); auto scriptString = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); - (*input.mutable_scripts())[hex(scriptHash.begin(), scriptHash.end())] = scriptString; + (*input.mutable_scripts())[hex(scriptHash)] = scriptString; auto utxo0 = input.add_utxo(); auto utxo0Script = Script(parse_hex("a9140055b0c94df477ee6b9f75185dfc9aa8ce2e52e487")); diff --git a/tests/chains/IoTeX/SignerTests.cpp b/tests/chains/IoTeX/SignerTests.cpp index 43a5c5ee22f..c0c78a5ea99 100644 --- a/tests/chains/IoTeX/SignerTests.cpp +++ b/tests/chains/IoTeX/SignerTests.cpp @@ -30,9 +30,9 @@ TEST(IoTeXSigner, Sign) { auto signer = IoTeX::Signer(std::move(input)); auto h = signer.hash(); - ASSERT_EQ(hex(h.begin(), h.end()), "0f17cd7f43bdbeff73dfe8f5cb0c0045f2990884e5050841de887cf22ca35b50"); + ASSERT_EQ(hex(h), "0f17cd7f43bdbeff73dfe8f5cb0c0045f2990884e5050841de887cf22ca35b50"); auto sig = signer.sign(); - ASSERT_EQ(hex(sig.begin(), sig.end()), "555cc8af4181bf85c044c3201462eeeb95374f78aa48c67b87510ee63d5e502372e53082f03e9a11c1e351de539cedf85d8dff87de9d003cb9f92243541541a000"); + ASSERT_EQ(hex(sig), "555cc8af4181bf85c044c3201462eeeb95374f78aa48c67b87510ee63d5e502372e53082f03e9a11c1e351de539cedf85d8dff87de9d003cb9f92243541541a000"); } TEST(IoTeXSigner, Build) { @@ -52,9 +52,9 @@ TEST(IoTeXSigner, Build) { auto signer = IoTeX::Signer(std::move(input)); auto output = signer.build(); auto encoded = output.encoded(); // signed action's serialized bytes - ASSERT_EQ(hex(encoded.begin(), encoded.end()), "0a4c0801107b18f8062203393939523e0a033435361229696f313837777a703038766e686a6a706b79646e723937716c68386b683064706b6b797466616d386a1a0c68656c6c6f20776f726c64211241044e18306ae9ef4ec9d07bf6e705442d4d1a75e6cdf750330ca2d880f2cc54607c9c33deb9eae9c06e06e04fe9ce3d43962cc67d5aa34fbeb71270d4bad3d648d91a41555cc8af4181bf85c044c3201462eeeb95374f78aa48c67b87510ee63d5e502372e53082f03e9a11c1e351de539cedf85d8dff87de9d003cb9f92243541541a000"); + ASSERT_EQ(hex(encoded), "0a4c0801107b18f8062203393939523e0a033435361229696f313837777a703038766e686a6a706b79646e723937716c68386b683064706b6b797466616d386a1a0c68656c6c6f20776f726c64211241044e18306ae9ef4ec9d07bf6e705442d4d1a75e6cdf750330ca2d880f2cc54607c9c33deb9eae9c06e06e04fe9ce3d43962cc67d5aa34fbeb71270d4bad3d648d91a41555cc8af4181bf85c044c3201462eeeb95374f78aa48c67b87510ee63d5e502372e53082f03e9a11c1e351de539cedf85d8dff87de9d003cb9f92243541541a000"); auto h = output.hash(); // signed action's hash - ASSERT_EQ(hex(h.begin(), h.end()), "6c84ac119058e859a015221f87a4e187c393d0c6ee283959342eac95fad08c33"); + ASSERT_EQ(hex(h), "6c84ac119058e859a015221f87a4e187c393d0c6ee283959342eac95fad08c33"); } } // namespace TW::IoTeX diff --git a/tests/chains/Ontology/Oep4Tests.cpp b/tests/chains/Ontology/Oep4Tests.cpp index be49303bd69..3a2513151ef 100644 --- a/tests/chains/Ontology/Oep4Tests.cpp +++ b/tests/chains/Ontology/Oep4Tests.cpp @@ -15,7 +15,7 @@ namespace TW::Ontology::tests { TEST(OntologyOep4, name) { std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); Oep4 wing(wing_addr); uint32_t nonce = 0x1234; @@ -26,7 +26,7 @@ TEST(OntologyOep4, name) { TEST(OntologyOep4, symbol) { std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); Oep4 wing(wing_addr); uint32_t nonce = 0x1234; @@ -37,7 +37,7 @@ TEST(OntologyOep4, symbol) { TEST(OntologyOep4, decimals) { std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); Oep4 wing(wing_addr); uint32_t nonce = 0x1234; @@ -48,7 +48,7 @@ TEST(OntologyOep4, decimals) { TEST(OntologyOep4, totalSupply) { std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); Oep4 wing(wing_addr); uint32_t nonce = 0x1234; @@ -59,7 +59,7 @@ TEST(OntologyOep4, totalSupply) { TEST(OntologyOep4, balanceOf) { std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); Oep4 wing(wing_addr); auto user = Address("AeaThtPwh5kAYnjHavzwmvxPd725nVTvbM"); @@ -101,7 +101,7 @@ TEST(OntologyOep4, transfer) { uint64_t gasLimit = 50000; std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); Oep4 wing(wing_addr); auto tx = wing.transfer(from, toAddress, amount, payer, gasPrice, gasLimit, nonce); @@ -127,7 +127,7 @@ TEST(OntologyOep4, transferMainnet) { // wing oep4 mainnet address std::string wing_hex{"00c59fcd27a562d6397883eab1f2fff56e58ef80"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); Oep4 wing(wing_addr); auto tx = wing.transfer(from, toAddress, amount, payer, gasPrice, gasLimit, nonce); diff --git a/tests/chains/Ontology/ParamsBuilderTests.cpp b/tests/chains/Ontology/ParamsBuilderTests.cpp index 0534472852d..c9a4bc02ae5 100644 --- a/tests/chains/Ontology/ParamsBuilderTests.cpp +++ b/tests/chains/Ontology/ParamsBuilderTests.cpp @@ -84,7 +84,7 @@ TEST(ParamsBuilder, transferInvokeCode) { TEST(ParamsBuilder, invokeOep4Code) { std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); NeoVmParamValue::ParamArray args{}; std::string method{"name"}; @@ -96,7 +96,7 @@ TEST(ParamsBuilder, invokeOep4Code) { TEST(ParamsBuilder, invokeOep4CodeBalanceOf) { std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); auto user_addr = Address("AeaThtPwh5kAYnjHavzwmvxPd725nVTvbM"); Data d(std::begin(user_addr._data), std::end(user_addr._data)); @@ -110,7 +110,7 @@ TEST(ParamsBuilder, invokeOep4CodeBalanceOf) { TEST(OntologyOep4, invokeOep4CodeTransfer) { std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; - auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto wing_addr = Address(parse_hex(wing_hex)); auto from = Address("APniYDGozkhUh8Tk7pe35aah2HGJ4fJfVd"); auto to = Address("AVY6LfvxauVQAVHDV9hC3ZCv7cQqzfDotH"); uint64_t amount = 253; diff --git a/tests/chains/Tezos/ForgingTests.cpp b/tests/chains/Tezos/ForgingTests.cpp index 66187be27d2..ab781bb4558 100644 --- a/tests/chains/Tezos/ForgingTests.cpp +++ b/tests/chains/Tezos/ForgingTests.cpp @@ -151,7 +151,7 @@ TEST(TezosTransaction, forgeTransaction) { auto expected = "6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e81020100008fb5cea62d147c696afd9a93dbce962f4c8a9c9100"; auto serialized = forgeOperation(transactionOperation); - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); + ASSERT_EQ(hex(serialized), expected); } TEST(TezosTransaction, forgeTransactionFA12) { @@ -222,7 +222,7 @@ TEST(TezosTransaction, forgeReveal) { auto expected = "6b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e"; auto serialized = forgeOperation(revealOperation); - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); + ASSERT_EQ(hex(serialized), expected); } TEST(TezosTransaction, forgeDelegate) { @@ -241,7 +241,7 @@ TEST(TezosTransaction, forgeDelegate) { auto expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e8102ff003e47f837f0467b4acde406ed5842f35e2414b1a8"; auto serialized = forgeOperation(delegateOperation); - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); + ASSERT_EQ(hex(serialized), expected); } TEST(TezosTransaction, forgeUndelegate) { @@ -260,7 +260,7 @@ TEST(TezosTransaction, forgeUndelegate) { auto expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200"; auto serialized = forgeOperation(delegateOperation); - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); + ASSERT_EQ(hex(serialized), expected); } } // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/OperationListTests.cpp b/tests/chains/Tezos/OperationListTests.cpp index 9fb89d9e8a6..17259ed9f41 100644 --- a/tests/chains/Tezos/OperationListTests.cpp +++ b/tests/chains/Tezos/OperationListTests.cpp @@ -43,7 +43,7 @@ TEST(TezosOperationList, ForgeOperationList_TransactionOnly) { auto expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e81020100008fb5cea62d147c696afd9a93dbce962f4c8a9c9100"; auto forged = op_list.forge(key); - ASSERT_EQ(hex(forged.begin(), forged.end()), expected); + ASSERT_EQ(hex(forged), expected); } TEST(TezosOperationList, ForgeOperationList_RevealOnly) { @@ -68,7 +68,7 @@ TEST(TezosOperationList, ForgeOperationList_RevealOnly) { auto expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e"; auto forged = op_list.forge(key); - ASSERT_EQ(hex(forged.begin(), forged.end()), expected); + ASSERT_EQ(hex(forged), expected); } TEST(TezosOperationList, ForgeOperationList_Delegation_ClearDelegate) { @@ -112,7 +112,7 @@ TEST(TezosOperationList, ForgeOperationList_Delegation_AddDelegate) { op_list.addOperation(delegationOperation); auto expected = "7105102c032807994dd9b5edf219261896a559876ca16cbf9d31dbe3612b89f26e00315b1206ec00b1b1e64cc3b8b93059f58fa2fc39e90944904e00ff00c4650fd609f88c67356e5fe01e37cd3ff654b18c"; auto forged = op_list.forge(key); - ASSERT_EQ(hex(forged.begin(), forged.end()), expected); + ASSERT_EQ(hex(forged), expected); } TEST(TezosOperationList, ForgeOperationList_TransactionAndReveal) { @@ -152,7 +152,7 @@ TEST(TezosOperationList, ForgeOperationList_TransactionAndReveal) { auto expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b003e47f837f0467b4acde406ed5842f35e2414b1a8f80992f001f44e810200603247bbf52501498293686da89ad8b2aca85f83b90903d4521dd2aba66054eb6c003e47f837f0467b4acde406ed5842f35e2414b1a8f80993f001f44e8102010000e42504da69a7c8d5baeaaeebe157a02db6b22ed800"; auto forged = op_list.forge(key); - ASSERT_EQ(hex(forged.begin(), forged.end()), expected); + ASSERT_EQ(hex(forged), expected); } TEST(TezosOperationList, ForgeOperationList_RevealWithoutPublicKey) { @@ -176,7 +176,7 @@ TEST(TezosOperationList, ForgeOperationList_RevealWithoutPublicKey) { auto expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b003e47f837f0467b4acde406ed5842f35e2414b1a8f80992f001f44e810200603247bbf52501498293686da89ad8b2aca85f83b90903d4521dd2aba66054eb"; auto forged = op_list.forge(key); - ASSERT_EQ(hex(forged.begin(), forged.end()), expected); + ASSERT_EQ(hex(forged), expected); } } // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/PublicKeyTests.cpp b/tests/chains/Tezos/PublicKeyTests.cpp index 66c7087dc7e..1048390b6c2 100644 --- a/tests/chains/Tezos/PublicKeyTests.cpp +++ b/tests/chains/Tezos/PublicKeyTests.cpp @@ -18,7 +18,7 @@ TEST(TezosPublicKey, forge) { auto input = parsePublicKey("edpkuAfEJCEatRgFpRGg3gn3FdWniLXBoubARreRwuVZPWufkgDBvR"); auto expected = "00451bde832454ba73e6e0de313fcf5d1565ec51080edc73bb19287b8e0ab2122b"; auto serialized = forgePublicKey(input); - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); + ASSERT_EQ(hex(serialized), expected); } TEST(TezosPublicKey, parse) { diff --git a/tests/chains/Tezos/SignerTests.cpp b/tests/chains/Tezos/SignerTests.cpp index 92681344646..eb024a36721 100644 --- a/tests/chains/Tezos/SignerTests.cpp +++ b/tests/chains/Tezos/SignerTests.cpp @@ -86,9 +86,9 @@ TEST(TezosSigner, SignOperationList) { std::string expectedSignedBytes = expectedForgedBytesToSign + expectedSignature; auto signedBytes = Signer().signOperationList(key, op_list); - auto signedBytesHex = hex(signedBytes.begin(), signedBytes.end()); + auto signedBytesHex = hex(signedBytes); - ASSERT_EQ(hex(signedBytes.begin(), signedBytes.end()), expectedSignedBytes); + ASSERT_EQ(hex(signedBytes), expectedSignedBytes); } } // namespace TW::Tezos::tests diff --git a/tests/chains/Zcash/TWZcashTransactionTests.cpp b/tests/chains/Zcash/TWZcashTransactionTests.cpp index b0acaf0f132..aae9fc24cd8 100644 --- a/tests/chains/Zcash/TWZcashTransactionTests.cpp +++ b/tests/chains/Zcash/TWZcashTransactionTests.cpp @@ -42,7 +42,7 @@ TEST(TWZcashTransaction, Encode) { auto unsignedData = Data{}; transaction.encode(unsignedData); - ASSERT_EQ(hex(unsignedData.begin(), unsignedData.end()), + ASSERT_EQ(hex(unsignedData), /* header */ "04000080" /* versionGroupId */ "85202f89" /* vin */ "01""a8c685478265f4c14dada651969c45a65e1aeb8cd6791f2f5bb6a1d9952104d9""01000000""6b483045022100a61e5d557568c2ddc1d9b03a7173c6ce7c996c4daecab007ac8f34bee01e6b9702204d38fdc0bcf2728a69fde78462a10fb45a9baa27873e6a5fc45fb5c76764202a01210365ffea3efa3908918a8b8627724af852fc9b86d7375b103ab0543cf418bcaa7f""feffffff" @@ -58,7 +58,7 @@ TEST(TWZcashTransaction, Encode) { auto scriptCode = Bitcoin::Script(parse_hex("76a914507173527b4c3318a2aecd793bf1cfed705950cf88ac")); auto preImage = transaction.getPreImage(scriptCode, 0, TWBitcoinSigHashTypeAll, 0x02faf080); - ASSERT_EQ(hex(preImage.begin(), preImage.end()), + ASSERT_EQ(hex(preImage), /* header */ "04000080" /* versionGroupId */ "85202f89" /* hashPrevouts */ "fae31b8dec7b0b77e2c8d6b6eb0e7e4e55abc6574c26dd44464d9408a8e33f11" @@ -78,7 +78,7 @@ TEST(TWZcashTransaction, Encode) { ); auto sighash = transaction.getSignatureHash(scriptCode, 0, TWBitcoinSigHashTypeAll, 0x02faf080, Bitcoin::BASE); - ASSERT_EQ(hex(sighash.begin(), sighash.end()), "f3148f80dfab5e573d5edfe7a850f5fd39234f80b5429d3a57edcc11e34c585b"); + ASSERT_EQ(hex(sighash), "f3148f80dfab5e573d5edfe7a850f5fd39234f80b5429d3a57edcc11e34c585b"); } TEST(TWZcashTransaction, SaplingSigning) { diff --git a/tests/chains/Zelcash/TWZelcashTransactionTests.cpp b/tests/chains/Zelcash/TWZelcashTransactionTests.cpp index 74f3413670e..bf90d832ae4 100644 --- a/tests/chains/Zelcash/TWZelcashTransactionTests.cpp +++ b/tests/chains/Zelcash/TWZelcashTransactionTests.cpp @@ -40,7 +40,7 @@ TEST(TWZelcashTransaction, Encode) { auto unsignedData = Data{}; transaction.encode(unsignedData); - ASSERT_EQ(hex(unsignedData.begin(), unsignedData.end()), + ASSERT_EQ(hex(unsignedData), /* header */ "04000080" /* versionGroupId */ "85202f89" /* vin */ "01""a8c685478265f4c14dada651969c45a65e1aeb8cd6791f2f5bb6a1d9952104d9""01000000""6b483045022100a61e5d557568c2ddc1d9b03a7173c6ce7c996c4daecab007ac8f34bee01e6b9702204d38fdc0bcf2728a69fde78462a10fb45a9baa27873e6a5fc45fb5c76764202a01210365ffea3efa3908918a8b8627724af852fc9b86d7375b103ab0543cf418bcaa7f""feffffff" @@ -56,7 +56,7 @@ TEST(TWZelcashTransaction, Encode) { auto scriptCode = Bitcoin::Script(parse_hex("76a914507173527b4c3318a2aecd793bf1cfed705950cf88ac")); auto preImage = transaction.getPreImage(scriptCode, 0, TWBitcoinSigHashTypeAll, 0x02faf080); - ASSERT_EQ(hex(preImage.begin(), preImage.end()), + ASSERT_EQ(hex(preImage), /* header */ "04000080" /* versionGroupId */ "85202f89" /* hashPrevouts */ "fae31b8dec7b0b77e2c8d6b6eb0e7e4e55abc6574c26dd44464d9408a8e33f11" @@ -76,7 +76,7 @@ TEST(TWZelcashTransaction, Encode) { ); auto sighash = transaction.getSignatureHash(scriptCode, 0, TWBitcoinSigHashTypeAll, 0x02faf080, Bitcoin::BASE); - ASSERT_EQ(hex(sighash.begin(), sighash.end()), "f3148f80dfab5e573d5edfe7a850f5fd39234f80b5429d3a57edcc11e34c585b"); + ASSERT_EQ(hex(sighash), "f3148f80dfab5e573d5edfe7a850f5fd39234f80b5429d3a57edcc11e34c585b"); } TEST(TWZelcashTransaction, Signing) { diff --git a/tests/chains/Zilliqa/SignerTests.cpp b/tests/chains/Zilliqa/SignerTests.cpp index 5c49cf46952..5e7ef3accc5 100644 --- a/tests/chains/Zilliqa/SignerTests.cpp +++ b/tests/chains/Zilliqa/SignerTests.cpp @@ -42,7 +42,7 @@ TEST(ZilliqaSigner, PreImage) { auto preImage = Signer::getPreImage(input, address); auto signature = Signer::sign(input).signature(); - ASSERT_EQ(hex(preImage.begin(), preImage.end()), "0881800410041a149ca91eb535fb92fda5094110fdaeb752edb9b03922230a21034ae47910d58b9bde819c3cffa8de4441955508db00aa2540db8e6bf6e99abc1b2a120a10000000000000000000000da475abf00032120a100000000000000000000000003b9aca003801"); + ASSERT_EQ(hex(preImage), "0881800410041a149ca91eb535fb92fda5094110fdaeb752edb9b03922230a21034ae47910d58b9bde819c3cffa8de4441955508db00aa2540db8e6bf6e99abc1b2a120a10000000000000000000000da475abf00032120a100000000000000000000000003b9aca003801"); ASSERT_TRUE(pubKey.verifyZilliqa(Data(signature.begin(), signature.end()), preImage)); } @@ -72,7 +72,7 @@ TEST(ZilliqaSigner, Signing) { auto output = Signer::sign(input); - ASSERT_EQ(hex(output.signature().begin(), output.signature().end()), "001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268"); + ASSERT_EQ(hex(output.signature()), "001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268"); ASSERT_EQ(output.json(), R"({"amount":"1000000000000","code":"","data":"","gasLimit":"1","gasPrice":"1000000000","nonce":2,"pubKey":"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c","signature":"001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268","toAddr":"7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C","version":65537})"); } @@ -105,7 +105,7 @@ TEST(ZilliqaSigner, SigningData) { auto output = Signer::sign(input); ASSERT_EQ(output.json(), R"({"amount":"10000000000000","code":"","data":"{\"_tag\":\"DelegateStake\",\"params\":[{\"type\":\"ByStr20\",\"value\":\"0x122219cCeAb410901e96c3A0e55E46231480341b\",\"vname\":\"ssnaddr\"}]}","gasLimit":"5000","gasPrice":"2000000000","nonce":56,"pubKey":"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c","signature":"437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d","toAddr":"43D459eC504C7432959c086B0ac7F7855E984306","version":65537})"); - ASSERT_EQ(hex(output.signature().begin(), output.signature().end()), "437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d"); + ASSERT_EQ(hex(output.signature()), "437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d"); } -} // namespace TW::Zilliqa::tests \ No newline at end of file +} // namespace TW::Zilliqa::tests diff --git a/tests/common/Bech32AddressTests.cpp b/tests/common/Bech32AddressTests.cpp index 7152fd9ce78..844d8a7276f 100644 --- a/tests/common/Bech32AddressTests.cpp +++ b/tests/common/Bech32AddressTests.cpp @@ -103,14 +103,14 @@ TEST(Bech32Address, FromPublicKey) { { auto privateKey = PrivateKey(parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - ASSERT_EQ(hex(publicKey.bytes.begin(), publicKey.bytes.end()), "026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502"); + ASSERT_EQ(hex(publicKey.bytes), "026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502"); auto address = Bech32Address("bnb", Hash::HasherSha256ripemd, publicKey); ASSERT_EQ("bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", address.string()); } { auto privateKey = PrivateKey(parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - ASSERT_EQ(hex(publicKey.bytes.begin(), publicKey.bytes.end()), "0257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc5"); + ASSERT_EQ(hex(publicKey.bytes), "0257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc5"); auto address = Bech32Address("cosmos", Hash::HasherSha256ripemd, publicKey); ASSERT_EQ(address.string(), "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); } @@ -129,7 +129,7 @@ TEST(Bech32Address, FromPublicKey) { { const auto privateKey = PrivateKey(parse_hex("3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - ASSERT_EQ(hex(publicKey.bytes.begin(), publicKey.bytes.end()), "02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d"); + ASSERT_EQ(hex(publicKey.bytes), "02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d"); const auto address = Bech32Address("zil", Hash::HasherSha256, publicKey); ASSERT_EQ("zil", address.getHrp()); ASSERT_EQ("zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg", address.string()); @@ -141,7 +141,7 @@ TEST(Bech32Address, Hashes) { const auto privateKey = PrivateKey(parse_hex("3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6")); auto publicKey1 = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - ASSERT_EQ("02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d", hex(publicKey1.bytes.begin(), publicKey1.bytes.end())); + ASSERT_EQ("02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d", hex(publicKey1.bytes)); const auto address1 = Bech32Address("hrp", Hash::HasherSha256ripemd, publicKey1); ASSERT_EQ("hrp186zwn9h0z9fyvwfqs4jl92cw3kexusm4xw6ptp", address1.string()); @@ -152,7 +152,7 @@ TEST(Bech32Address, Hashes) { auto publicKey2 = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); ASSERT_EQ( "04b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d83c307736082c09f1f22328e0fbeab40ddd198cf0f70fcdaa1e5969ca400c098", - hex(publicKey2.bytes.begin(), publicKey2.bytes.end())); + hex(publicKey2.bytes)); const auto address3 = Bech32Address("hrp", Hash::HasherKeccak256, publicKey2); ASSERT_EQ("hrp17hff3s97m5uxpjcdq3nzqxxatt8cmumnsf03su", address3.string()); @@ -163,7 +163,7 @@ TEST(Bech32Address, Prefixes) { const auto privateKey = PrivateKey(parse_hex("3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - ASSERT_EQ("02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d", hex(publicKey.bytes.begin(), publicKey.bytes.end())); + ASSERT_EQ("02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d", hex(publicKey.bytes)); const auto address1 = Bech32Address("hrpone", Hash::HasherSha256ripemd, publicKey); ASSERT_EQ("hrpone186zwn9h0z9fyvwfqs4jl92cw3kexusm47das6p", address1.string()); diff --git a/tools/generate-files b/tools/generate-files index 70825c15aaa..ee90c777168 100755 --- a/tools/generate-files +++ b/tools/generate-files @@ -53,10 +53,10 @@ codegen/bin/codegen tools/doxygen_convert_comments # Generate rust bindgen -tools/rust-bindgen +tools/rust-bindgen $1 # Generate Java, C++ and Swift Protobuf files -if [ -x "$(command -v protoc-gen-swift)" ]; then +if [ -x "$(command -v protoc-gen-swift)" ] && [ $# -eq 0 ]; then "$PROTOC" -I=$PREFIX/include -I=src/proto --cpp_out=src/proto --java_out=lite:jni/java --swift_out=swift/Sources/Generated/Protobuf --swift_opt=Visibility=Public src/proto/*.proto else "$PROTOC" -I=$PREFIX/include -I=src/proto --cpp_out=src/proto --java_out=lite:jni/java src/proto/*.proto @@ -74,11 +74,13 @@ fi "$PROTOC" -I=$PREFIX/include -I=src/proto --plugin=$PREFIX/bin/protoc-gen-swift-typealias --swift-typealias_out swift/Sources/Generated/Protobuf src/proto/*.proto # Generate Xcode project -if [ -x "$(command -v xcodegen)" ]; then +if [ -x "$(command -v xcodegen)" ] && [ $# -eq 0 ]; then pushd swift xcodegen pod install popd +elif [ "$1" == "android" ]; then + echo -e "\nWARNING: Android detected, skipping xcodegen generation" else echo -e "\nWARNING: Skipped generating Xcode project because the xcodegen tool is not installed." fi diff --git a/tools/rust-bindgen b/tools/rust-bindgen index 044e16faf6a..6c8350cf7f4 100755 --- a/tools/rust-bindgen +++ b/tools/rust-bindgen @@ -4,7 +4,7 @@ set -e TARGET_NAME="libwallet_core_rs.a" TARGET_XCFRAMEWORK_NAME=../swift/WalletCoreRs.xcframework -BUILD_FOLDER=../build/local +BUILD_FOLDER=../rust/target CRATE="wallet-core-rs" HEADER_NAME="WalletCoreRSBindgen.h" @@ -17,36 +17,33 @@ create_xc_framework() { cd rust -echo "Generating Native targets" -CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --release -CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target wasm32-unknown-emscripten --release +if [[ "$1" != "android" ]]; then + echo "Generating Native targets" + cargo build --release + cargo build --target wasm32-unknown-emscripten --release +fi if [[ `uname` == "Darwin" ]]; then echo "Generating Android targets" - CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --target aarch64-linux-android --release - CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --target armv7-linux-androideabi --release - CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --target x86_64-linux-android --release - CARGO_TARGET_DIR=$BUILD_FOLDER/ cargo build --target i686-linux-android --release - echo "Generating iOS targets" - CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target aarch64-apple-ios --release - CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target aarch64-apple-ios-sim --release - CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target x86_64-apple-ios --release - CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target aarch64-apple-darwin --release - CARGO_TARGET_DIR=$BUILD_FOLDER cargo build --target x86_64-apple-darwin --release - CARGO_TARGET_DIR=$BUILD_FOLDER cargo +nightly build -Z build-std --target aarch64-apple-ios-macabi --release - CARGO_TARGET_DIR=$BUILD_FOLDER cargo +nightly build -Z build-std --target x86_64-apple-ios-macabi --release - lipo $BUILD_FOLDER/x86_64-apple-ios/release/$TARGET_NAME $BUILD_FOLDER/aarch64-apple-ios-sim/release/$TARGET_NAME -create -output $BUILD_FOLDER/$TARGET_NAME - mkdir -p $BUILD_FOLDER/darwin_universal - lipo $BUILD_FOLDER/x86_64-apple-darwin/release/$TARGET_NAME $BUILD_FOLDER/aarch64-apple-darwin/release/$TARGET_NAME -create -output $BUILD_FOLDER/darwin_universal/$TARGET_NAME - mkdir -p $BUILD_FOLDER/catalyst - lipo $BUILD_FOLDER/aarch64-apple-ios-macabi/release/$TARGET_NAME $BUILD_FOLDER/x86_64-apple-ios-macabi/release/$TARGET_NAME -create -output $BUILD_FOLDER/catalyst/$TARGET_NAME + cargo build --target aarch64-linux-android --target armv7-linux-androideabi --target x86_64-linux-android --target i686-linux-android --release + if [[ "$1" != "android" ]]; then + echo "Generating iOS targets" + cargo build --target aarch64-apple-ios --target aarch64-apple-ios-sim --target x86_64-apple-ios --target aarch64-apple-darwin --target x86_64-apple-darwin --release & + cargo +nightly build -Z build-std --target aarch64-apple-ios-macabi --target x86_64-apple-ios-macabi --release & + wait + lipo $BUILD_FOLDER/x86_64-apple-ios/release/$TARGET_NAME $BUILD_FOLDER/aarch64-apple-ios-sim/release/$TARGET_NAME -create -output $BUILD_FOLDER/$TARGET_NAME + mkdir -p $BUILD_FOLDER/darwin_universal + lipo $BUILD_FOLDER/x86_64-apple-darwin/release/$TARGET_NAME $BUILD_FOLDER/aarch64-apple-darwin/release/$TARGET_NAME -create -output $BUILD_FOLDER/darwin_universal/$TARGET_NAME + mkdir -p $BUILD_FOLDER/catalyst + lipo $BUILD_FOLDER/aarch64-apple-ios-macabi/release/$TARGET_NAME $BUILD_FOLDER/x86_64-apple-ios-macabi/release/$TARGET_NAME -create -output $BUILD_FOLDER/catalyst/$TARGET_NAME - create_xc_framework + create_xc_framework + fi fi cbindgen --crate $CRATE --output ../src/rust/bindgen/$HEADER_NAME cd - -cp build/local/release/${TARGET_NAME} build/local/lib/ +[[ -e rust/target/release/${TARGET_NAME} ]] && cp rust/target/release/${TARGET_NAME} build/local/lib/ -if [[ `uname` == "Darwin" ]]; then +if [[ `uname` == "Darwin" ]] && [[ "$1" != "android" ]]; then cd rust cat > $TARGET_XCFRAMEWORK_NAME/Info.plist << EOF From 428290cc05ee035a1944c084317011505127c773 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 14 Feb 2023 14:13:08 +0100 Subject: [PATCH 101/426] [Rust/Base32]: Encoding/Decoding support (#2923) --- rust/src/encoding/base32.rs | 250 ++++++++++++++++++++++++++++++++++++ rust/src/encoding/mod.rs | 1 + src/Base32.h | 45 +++---- src/Base64.cpp | 2 +- 4 files changed, 267 insertions(+), 31 deletions(-) create mode 100644 rust/src/encoding/base32.rs diff --git a/rust/src/encoding/base32.rs b/rust/src/encoding/base32.rs new file mode 100644 index 00000000000..2d56b74c767 --- /dev/null +++ b/rust/src/encoding/base32.rs @@ -0,0 +1,250 @@ +// Copyright © 2017-2023 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. + +use std::ffi::{c_char, CStr, CString}; +use crate::memory::CByteArray; + +const ALPHABET_RFC4648: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +pub fn get_alphabet(alphabet: *const c_char) -> Option<&'static [u8]> { + let alphabet = match alphabet.is_null() { + false => { + let alphabet = unsafe { CStr::from_ptr(alphabet).to_str().unwrap().as_bytes() }; + Some(alphabet) + } + true => None, + }; + alphabet +} + +fn base32_encode(input: &[u8], alphabet: Option<&[u8]>, padding: bool) -> Result { + let alphabet = alphabet.unwrap_or(ALPHABET_RFC4648); + if alphabet.len() != 32 { + return Err("Invalid alphabet: must contain 32 characters".to_string()); + } + + let mut result = String::new(); + let mut buffer: u32 = 0; + let mut buffer_size = 0; + + for &byte in input { + buffer = (buffer << 8) | u32::from(byte); + buffer_size += 8; + + while buffer_size >= 5 { + result.push(char::from(alphabet[(buffer >> (buffer_size - 5)) as usize & 31])); + buffer_size -= 5; + } + } + + if buffer_size > 0 { + result.push(char::from(alphabet[(buffer << (5 - buffer_size)) as usize & 31])); + } + + if padding { + let padding = 8 - (result.len() % 8); + result.extend(std::iter::repeat('=').take(padding)); + } + + Ok(result) +} + +#[no_mangle] +pub extern "C" fn encode_base32(input: *const u8, input_len: usize, alphabet: *const c_char, padding: bool) -> *mut c_char { + let input = unsafe { std::slice::from_raw_parts(input, input_len) }; + + let alphabet = get_alphabet(alphabet); + + match base32_encode(input, alphabet, padding) { + Ok(result) => CString::new(result).unwrap().into_raw(), + Err(_) => std::ptr::null_mut(), + } +} + +fn base32_decode(input: &str, alphabet: Option<&[u8]>, padding: bool) -> Result, String> { + let alphabet = alphabet.unwrap_or(ALPHABET_RFC4648); + let mut output = Vec::new(); + let mut buffer: u32 = 0; + let mut bits_left = 0; + let alphabet_map: std::collections::HashMap = alphabet.iter().enumerate().map(|(i, &c)| (c, i as u32)).collect(); + let input = if padding { + input.trim_end_matches('=') + } else { + input + }; + + for c in input.bytes() { + let val = match alphabet_map.get(&c) { + Some(val) => *val, + None => return Err("Invalid character in input".to_string()), + }; + buffer = (buffer << 5) | val; + bits_left += 5; + if bits_left >= 8 { + output.push((buffer >> (bits_left - 8)) as u8); + bits_left -= 8; + } + } + + if padding && bits_left >= 5 { + return Err("Invalid padding in input".to_string()); + } + + if output == vec![0] { + return Ok(vec![]) + } + Ok(output) +} + +#[no_mangle] +pub extern "C" fn decode_base32(input: *const c_char, alphabet: *const c_char, padding: bool) -> CByteArray { + let input = unsafe { CStr::from_ptr(input).to_str().unwrap() }; + let alphabet = get_alphabet(alphabet); + + match base32_decode(input, alphabet, padding) { + Ok(decoded) => { + let size = decoded.len(); + let mut decoded_vec = decoded.to_vec(); + let ptr = decoded_vec.as_mut_ptr(); + std::mem::forget(decoded_vec); + CByteArray { data: ptr, size } + }, + Err(_) => { + CByteArray { data: std::ptr::null_mut(), size: 0 } + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_base32_encode() { + let data = b"Hello, world!"; + let expected = "JBSWY3DPFQQHO33SNRSCC"; + + let result = base32_encode(data, None, false).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn test_base32_encode_padding() { + let data = b"Hello, world!"; + let expected = "JBSWY3DPFQQHO33SNRSCC==="; + + let result = base32_encode(data, None, true).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn test_base32_encode_filecoin() { + let alphabet = "abcdefghijklmnopqrstuvwxyz234567"; + let data = b"7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy"; + let expected = "g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i"; + let result = base32_encode(data, Some(alphabet.as_bytes()), false).unwrap(); + assert_eq!(result, expected); + + let invalid_alphabet = "invalidalphabet"; + let result = base32_encode(data, Some(invalid_alphabet.as_bytes()), false); + assert_eq!(result.is_err(), true); + } + + #[test] + fn test_base32_encode_ffi() { + let data = b"Hello, world!"; + let expected_1 = "JBSWY3DPFQQHO33SNRSCC==="; + let expected_2 = "JBSWY3DPFQQHO33SNRSCC"; + let expected_3 = "jbswy3dpfqqho33snrscc==="; + let expected_4 = "jbswy3dpfqqho33snrscc"; + + let result_ptr = encode_base32(data.as_ptr(), data.len(), std::ptr::null(), true); + let result = unsafe { CString::from_raw(result_ptr) }; + assert_eq!(result.to_str().unwrap(), expected_1); + + let result_ptr = encode_base32(data.as_ptr(), data.len(), std::ptr::null(), false); + let result = unsafe { CString::from_raw(result_ptr) }; + assert_eq!(result.to_str().unwrap(), expected_2); + + let alphabet = CString::new("abcdefghijklmnopqrstuvwxyz234567").unwrap(); + let result_ptr = encode_base32(data.as_ptr(), data.len(), alphabet.as_ptr(), true); + let result = unsafe { CString::from_raw(result_ptr) }; + assert_eq!(result.to_str().unwrap(), expected_3); + + let result_ptr = encode_base32(data.as_ptr(), data.len(), alphabet.as_ptr(), false); + let result = unsafe { CString::from_raw(result_ptr) }; + assert_eq!(result.to_str().unwrap(), expected_4); + } + + #[test] + fn test_base32_decode() { + let data = "JBSWY3DPFQQHO33SNRSCC"; + let expected = b"Hello, world!"; + + let result = base32_decode(data, None, false).unwrap(); + assert_eq!(result.as_slice(), expected); + } + + #[test] + fn test_base32_decode_abc() { + let data = "ABC"; + let expected = b""; + + let result = base32_decode(data, None, false).unwrap(); + assert_eq!(result.as_slice(), expected); + } + + #[test] + fn test_base32_decode_padding() { + let data = "JBSWY3DPFQQHO33SNRSCC==="; + let expected = b"Hello, world!"; + + let result = base32_decode(data, None, true).unwrap(); + assert_eq!(result.as_slice(), expected); + } + + #[test] + fn test_base32_decode_filecoin() { + let alphabet = "abcdefghijklmnopqrstuvwxyz234567"; + let data = "g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i"; + let expected = b"7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy"; + + let result = base32_decode(data, Some(alphabet.as_bytes()), false).unwrap(); + assert_eq!(result.as_slice(), expected); + } + + #[test] + fn test_base32_decode_ffi() { + let input_1 = "JBSWY3DPFQQHO33SNRSCC==="; + let input_2 = "JBSWY3DPFQQHO33SNRSCC"; + let input_3 = "jbswy3dpfqqho33snrscc==="; + let input_4 = "jbswy3dpfqqho33snrscc"; + let expected = b"Hello, world!"; + + + let input_1 = CString::new(input_1).unwrap(); + let decoded_ptr = decode_base32(input_1.as_ptr(), std::ptr::null(), true); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + assert_eq!(decoded_slice, expected); + + let input_2 = CString::new(input_2).unwrap(); + let decoded_ptr = decode_base32(input_2.as_ptr(), std::ptr::null(), false); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + assert_eq!(decoded_slice, expected); + + + let alphabet = CString::new("abcdefghijklmnopqrstuvwxyz234567").unwrap(); + let input_3 = CString::new(input_3).unwrap(); + let decoded_ptr = decode_base32(input_3.as_ptr(), alphabet.as_ptr(), true); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + assert_eq!(decoded_slice, expected); + + let input_4 = CString::new(input_4).unwrap(); + let decoded_ptr = decode_base32(input_4.as_ptr(), alphabet.as_ptr(), false); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + assert_eq!(decoded_slice, expected); + } +} diff --git a/rust/src/encoding/mod.rs b/rust/src/encoding/mod.rs index b1798fbcce7..f0648e99e5a 100644 --- a/rust/src/encoding/mod.rs +++ b/rust/src/encoding/mod.rs @@ -4,5 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +mod base32; mod base64; mod hex; diff --git a/src/Base32.h b/src/Base32.h index 7119a9944be..bf4508f37ec 100644 --- a/src/Base32.h +++ b/src/Base32.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,8 +7,7 @@ #pragma once #include "Data.h" - -#include +#include "rust/bindgen/WalletCoreRSBindgen.h" #include @@ -17,41 +16,27 @@ namespace TW::Base32 { /// Decode Base32 string, return bytes as Data /// alphabet: Optional alphabet, if missing, default ALPHABET_RFC4648 inline bool decode(const std::string& encoded_in, Data& decoded_out, const char* alphabet_in = nullptr) { - size_t inLen = encoded_in.size(); - // obtain output length first - size_t outLen = base32_decoded_length(inLen); - uint8_t buf[outLen]; - if (alphabet_in == nullptr) { - alphabet_in = BASE32_ALPHABET_RFC4648; + if (encoded_in.empty()) { + return true; } - // perform the base32 decode - uint8_t* retval = base32_decode(encoded_in.data(), inLen, buf, outLen, alphabet_in); - if (retval == nullptr) { + auto decoded = decode_base32(encoded_in.c_str(), alphabet_in, false); + if (decoded.data == nullptr || decoded.size == 0) { return false; } - decoded_out.assign(buf, buf + outLen); + Data decoded_vec(&decoded.data[0], &decoded.data[decoded.size]); + std::free(decoded.data); + decoded_out = decoded_vec; return true; } + /// Encode bytes in Data to Base32 string /// alphabet: Optional alphabet, if missing, default ALPHABET_RFC4648 -inline std::string encode(const Data& val, const char* alphabet = nullptr) { - size_t inLen = val.size(); - // obtain output length first, reserve for terminator - size_t outLen = base32_encoded_length(inLen) + 1; - char buf[outLen]; - if (alphabet == nullptr) { - alphabet = BASE32_ALPHABET_RFC4648; - } - // perform the base32 encode - char* retval = base32_encode(val.data(), inLen, buf, outLen, alphabet); - if (retval == nullptr) { - // return empty string if failed - return std::string(); - } - // make sure there is a terminator ath the end - buf[outLen - 1] = '\0'; - return std::string(buf); +inline std::string encode(const Data& val, const char* alphabet = nullptr, bool padding = false) { + auto* encoded = encode_base32(val.data(), val.size(), alphabet, padding); + std::string encoded_str(encoded); + free_string(encoded); + return encoded_str; } } // namespace TW::Base32 diff --git a/src/Base64.cpp b/src/Base64.cpp index a09f399e3c7..140ca2d4a76 100644 --- a/src/Base64.cpp +++ b/src/Base64.cpp @@ -21,7 +21,7 @@ Data decode(const std::string& val, bool is_url) { return Data(); } auto decoded = decode_base64(val.c_str(), is_url); - if (decoded.data == nullptr) { + if (decoded.data == nullptr || decoded.size == 0) { return Data(); } std::vector decoded_vec(&decoded.data[0], &decoded.data[decoded.size]); From 9998a8a48ee20d964c4fe8d81821547ae5a04d32 Mon Sep 17 00:00:00 2001 From: Maxim Pestryakov Date: Tue, 14 Feb 2023 21:29:51 +0800 Subject: [PATCH 102/426] [Kotlin] Fix CMakeLists for Android (#2929) --- CMakeLists.txt | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64bb0147fb9..5828285dc27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,22 +53,27 @@ if (${ANDROID}) message("Configuring for JNI") file(GLOB_RECURSE core_sources src/*.c src/*.cc src/*.cpp src/*.h jni/cpp/*.c jni/cpp/*.cpp jni/cpp/*.h) if (${KOTLIN}) - file(GLOB_RECURSE specific_sources jni/kotlin/*.h jni/kotlin/*.c) + file(GLOB_RECURSE specific_sources + jni/kotlin/*.h + jni/kotlin/*.c + kotlin/wallet-core-kotlin/src/androidMain/cpp/generated/*.h + kotlin/wallet-core-kotlin/src/androidMain/cpp/generated/*.c + ) else () file(GLOB_RECURSE specific_sources jni/android/*.h jni/android/*.c) - endif() + endif () set(sources ${core_sources} ${specific_sources}) add_library(TrustWalletCore SHARED ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) find_library(log-lib log) if (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a") set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/aarch64-linux-android/release/${WALLET_CORE_RS_LIB}) - elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86") + elseif (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86") set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/i686-linux-android/release/${WALLET_CORE_RS_LIB}) - elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a") + elseif (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a") set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/armv7-linux-androideabi/release/${WALLET_CORE_RS_LIB}) - elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86_64") + elseif (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86_64") set(WALLET_CORE_BINDGEN ${WALLET_CORE_RS_TARGET_DIR}/x86_64-linux-android/release/${WALLET_CORE_RS_LIB}) - endif() + endif () target_link_libraries(TrustWalletCore PUBLIC ${WALLET_CORE_BINDGEN} ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto protobuf ${log-lib} Boost::boost) else () message("Configuring standalone") @@ -118,11 +123,11 @@ if (TW_UNITY_BUILD) set_target_properties(TrustWalletCore PROPERTIES UNITY_BUILD ON) file(GLOB_RECURSE PROTOBUF_SOURCE_FILES CONFIGURE_DEPENDS src/Cosmos/Protobuf/*.pb.cc src/Hedera/Protobuf/*.pb.cc src/proto/*.pb.cc) - foreach(file ${PROTOBUF_SOURCE_FILES}) + foreach (file ${PROTOBUF_SOURCE_FILES}) set_property(SOURCE ${file} PROPERTY SKIP_UNITY_BUILD_INCLUSION ON) - endforeach() + endforeach () message(STATUS "Unity build activated") -endif() +endif () configure_file(${CMAKE_CURRENT_SOURCE_DIR}/swift/cpp.xcconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/swift/cpp.xcconfig @ONLY) From 732566902f5fe33e7b52e1b3f48bad1251da9c0d Mon Sep 17 00:00:00 2001 From: Maxim Pestryakov Date: Wed, 15 Feb 2023 18:22:42 +0800 Subject: [PATCH 103/426] [Kotlin] Removed :generateCinterop task (#2934) --- kotlin/README.md | 2 -- ...=> convention.proto-generation.gradle.kts} | 30 ------------------- kotlin/wallet-core-kotlin/.gitignore | 1 - kotlin/wallet-core-kotlin/build.gradle.kts | 7 +++-- .../src/nativeInterop/cinterop/WalletCore.def | 0 tools/kotlin-build | 2 +- tools/kotlin-release | 2 +- 7 files changed, 6 insertions(+), 38 deletions(-) rename kotlin/build-logic/src/main/kotlin/{convention.file-generation.gradle.kts => convention.proto-generation.gradle.kts} (65%) create mode 100644 kotlin/wallet-core-kotlin/src/nativeInterop/cinterop/WalletCore.def diff --git a/kotlin/README.md b/kotlin/README.md index 42e4cf78a39..d80405f6046 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -1,5 +1,3 @@ ### Tasks: - `./gradlew :wallet-core-kotlin:generateProtos` – Generates Kotlin classes for Protos -- `./gradlew :wallet-core-kotlin:generateCinterop` – Generates def file -- `./gradlew :wallet-core-kotlin:generateFiles` – Generates all the above and Kotlin accessors to the library diff --git a/kotlin/build-logic/src/main/kotlin/convention.file-generation.gradle.kts b/kotlin/build-logic/src/main/kotlin/convention.proto-generation.gradle.kts similarity index 65% rename from kotlin/build-logic/src/main/kotlin/convention.file-generation.gradle.kts rename to kotlin/build-logic/src/main/kotlin/convention.proto-generation.gradle.kts index 028fb3a9c2f..7775d61e4ed 100644 --- a/kotlin/build-logic/src/main/kotlin/convention.file-generation.gradle.kts +++ b/kotlin/build-logic/src/main/kotlin/convention.proto-generation.gradle.kts @@ -1,27 +1,5 @@ val libs = extensions.getByType().named("libs") -val generateCinteropTask = task("generateCinterop") { - doFirst { - val headersDir = rootDir.parentFile.resolve("include/TrustWalletCore") - val headers = headersDir - .listFiles { file -> file.extension == "h" } - .orEmpty() - .sortedBy { it.name } - .joinToString(separator = " ") { it.name } - - val defFile = projectDir.resolve("src/nativeInterop/cinterop/walletCore.def") - defFile.parentFile.mkdirs() - defFile.writeText( - text = - """ - headers = $headers - package = com.trustwallet.core - - """.trimIndent(), - ) - } -} - val copyProtoTask = task("copyProtos") { val sourceDir = rootDir.parentFile.resolve("src/proto") val destinationDir = projectDir.resolve("build/tmp/proto") @@ -76,11 +54,3 @@ val generateProtosTask = task("generateProtos") { "--kotlin_out=$destinationDir", ) } - -task("generateFiles") { - dependsOn(generateCinteropTask) - dependsOn(generateProtosTask) - - workingDir(rootDir.parentFile) - commandLine("./codegen/bin/codegen") -} diff --git a/kotlin/wallet-core-kotlin/.gitignore b/kotlin/wallet-core-kotlin/.gitignore index 1f22b7e6731..a9e76bfd475 100644 --- a/kotlin/wallet-core-kotlin/.gitignore +++ b/kotlin/wallet-core-kotlin/.gitignore @@ -1,3 +1,2 @@ /src/**/generated/ /src/**/proto/ -/src/nativeInterop/ diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts index 4af9f55bf0e..780048b49fa 100644 --- a/kotlin/wallet-core-kotlin/build.gradle.kts +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -6,7 +6,7 @@ plugins { kotlin("multiplatform") id("com.android.library") id("convention.maven-publish") - id("convention.file-generation") + id("convention.proto-generation") } kotlin { @@ -71,8 +71,9 @@ kotlin { nativeTargets.forEach { nativeTarget -> nativeTarget.apply { val main by compilations.getting - val walletCore by main.cinterops.creating { - includeDirs.allHeaders(rootDir.parentFile.resolve("include/TrustWalletCore")) + main.cinterops.create("WalletCore") { + packageName = "com.trustwallet.core" + headers(rootDir.parentFile.resolve("include/TrustWalletCore").listFiles()!!) } } } diff --git a/kotlin/wallet-core-kotlin/src/nativeInterop/cinterop/WalletCore.def b/kotlin/wallet-core-kotlin/src/nativeInterop/cinterop/WalletCore.def new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/kotlin-build b/tools/kotlin-build index 4eb14383f65..a64537b101f 100755 --- a/tools/kotlin-build +++ b/tools/kotlin-build @@ -3,6 +3,6 @@ set -e pushd kotlin -./gradlew :wallet-core-kotlin:generateFiles +./gradlew :wallet-core-kotlin:generateProtos ./gradlew :wallet-core-kotlin:assemble popd diff --git a/tools/kotlin-release b/tools/kotlin-release index 0eadf3fe30c..ce934855911 100755 --- a/tools/kotlin-release +++ b/tools/kotlin-release @@ -10,7 +10,7 @@ echo "Building $version" export ANDROID_HOME="$HOME/Library/Android/sdk" pushd kotlin -./gradlew :wallet-core-kotlin:generateFiles +./gradlew :wallet-core-kotlin:generateProtos ./gradlew :wallet-core-kotlin:assemble :wallet-core-kotlin:publish -Pversion="$version" popd From a118655786c3f204a46797ad36d223d1d933f97d Mon Sep 17 00:00:00 2001 From: Vladimir Miloserdov Date: Fri, 17 Feb 2023 10:14:20 +0000 Subject: [PATCH 104/426] [ZkSync]: Add support for ZkSync Era mainnet (#2768) --- docs/registry.md | 2 +- registry.json | 12 ++++++------ tests/chains/ZkSyncV2/TWCoinTypeTests.cpp | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/registry.md b/docs/registry.md index 65da87087a0..e1a73222d10 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -87,9 +87,9 @@ This list is generated from [./registry.json](../registry.json) | 10000118 | Osmosis | OSMO | | | | 10000145 | Smart Bitcoin Cash | BCH | | | | 10000250 | Fantom | FTM | | | -| 10000280 | zkSync v2 | ETH | | | | 10000288 | Boba | BOBAETH | | | | 10000321 | KuCoin Community Chain | KCS | | | +| 10000324 | zkSync Era | ETH | | | | 10000330 | Terra | LUNA | | | | 10000553 | Huobi ECO Chain | HT | | | | 10001088 | Metis | METIS | | | diff --git a/registry.json b/registry.json index 869c99c92bc..24f21c446ac 100644 --- a/registry.json +++ b/registry.json @@ -2126,8 +2126,8 @@ { "id": "zksync", "name": "Zksync", - "displayName": "zkSync v2", - "coinId": 10000280, + "displayName": "zkSync Era", + "coinId": 10000324, "slip44": 60, "symbol": "ETH", "decimals": 18, @@ -2139,18 +2139,18 @@ ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", - "chainId": "280", + "chainId": "324", "addressHasher": "keccak256", "explorer": { - "url": "https://zksync2-testnet.zkscan.io", + "url": "https://explorer.zksync.io", "txPath": "/tx/", "accountPath": "/address/" }, "info": { "url": "https://portal.zksync.io/", "source": "https://github.com/matter-labs/zksync", - "rpc": "https://zksync2-testnet.zksync.dev", - "documentation": "https://v2-docs.zksync.io" + "rpc": "https://zksync2-mainnet.zksync.io", + "documentation": "https://era.zksync.io/docs" } }, { diff --git a/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp b/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp index 95b4854e53e..64b719a3f54 100644 --- a/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp +++ b/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp @@ -16,21 +16,21 @@ TEST(TWZksyncCoinType, TWCoinType) { const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); const auto chainId = WRAPS(TWCoinTypeChainId(coin)); - const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb526861291c0335435e3c976e672a464b70762e54d7167409fb4f66e374ed708")); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xae38d3ede1104d088b474da261d0eb4847952c3db24c21e820502f4c1b0c01f5")); const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); - const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x970978989a51790ee591b2a54f92c7cd9cdc2f88")); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xeF86b2c8740518548ae449c4C3892B4be0475d8c")); const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); assertStringsEqual(id, "zksync"); - assertStringsEqual(name, "zkSync v2"); + assertStringsEqual(name, "zkSync Era"); assertStringsEqual(symbol, "ETH"); ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); - assertStringsEqual(chainId, "280"); - assertStringsEqual(txUrl, "https://zksync2-testnet.zkscan.io/tx/0xb526861291c0335435e3c976e672a464b70762e54d7167409fb4f66e374ed708"); - assertStringsEqual(accUrl, "https://zksync2-testnet.zkscan.io/address/0x970978989a51790ee591b2a54f92c7cd9cdc2f88"); + assertStringsEqual(chainId, "324"); + assertStringsEqual(txUrl, "https://explorer.zksync.io/tx/0xae38d3ede1104d088b474da261d0eb4847952c3db24c21e820502f4c1b0c01f5"); + assertStringsEqual(accUrl, "https://explorer.zksync.io/address/0xeF86b2c8740518548ae449c4C3892B4be0475d8c"); } } // namespace TW::TWZksync::tests From 37d5c1cdbe1c30cdc36d0e16fc188fb66bf1bf07 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 20 Feb 2023 10:19:49 +0100 Subject: [PATCH 105/426] [Solana]: Versioned Tx (#2935) --- coverage.stats | 2 +- src/Solana/AccountMeta.h | 20 + src/Solana/Address.h | 2 +- src/Solana/AddressLookupTable.h | 20 + src/Solana/CompiledInstruction.cpp | 22 ++ src/Solana/CompiledInstruction.h | 38 ++ src/Solana/Constants.h | 27 ++ src/Solana/Encoding.h | 31 ++ src/Solana/Instruction.h | 136 +++++++ src/Solana/LegacyMessage.cpp | 99 +++++ src/Solana/LegacyMessage.h | 253 ++++++++++++ src/Solana/MessageHeader.h | 22 ++ src/Solana/Program.cpp | 4 +- src/Solana/Program.h | 2 +- src/Solana/Signature.cpp | 13 + src/Solana/Signature.h | 30 ++ src/Solana/Signer.cpp | 36 +- src/Solana/Signer.h | 8 +- src/Solana/Transaction.cpp | 107 +---- src/Solana/Transaction.h | 478 +---------------------- src/Solana/V0Message.h | 19 + src/Solana/VersionedMessage.cpp | 67 ++++ src/Solana/VersionedMessage.h | 21 + src/Solana/VersionedTransaction.cpp | 37 ++ src/Solana/VersionedTransaction.h | 46 +++ src/proto/Solana.proto | 20 +- tests/chains/Solana/ProgramTests.cpp | 4 +- tests/chains/Solana/SignerTests.cpp | 52 +-- tests/chains/Solana/TWAnySignerTests.cpp | 18 + tests/chains/Solana/TransactionTests.cpp | 26 +- 30 files changed, 1012 insertions(+), 648 deletions(-) create mode 100644 src/Solana/AccountMeta.h create mode 100644 src/Solana/AddressLookupTable.h create mode 100644 src/Solana/CompiledInstruction.cpp create mode 100644 src/Solana/CompiledInstruction.h create mode 100644 src/Solana/Constants.h create mode 100644 src/Solana/Encoding.h create mode 100644 src/Solana/Instruction.h create mode 100644 src/Solana/LegacyMessage.cpp create mode 100644 src/Solana/LegacyMessage.h create mode 100644 src/Solana/MessageHeader.h create mode 100644 src/Solana/Signature.cpp create mode 100644 src/Solana/Signature.h create mode 100644 src/Solana/V0Message.h create mode 100644 src/Solana/VersionedMessage.cpp create mode 100644 src/Solana/VersionedMessage.h create mode 100644 src/Solana/VersionedTransaction.cpp create mode 100644 src/Solana/VersionedTransaction.h diff --git a/coverage.stats b/coverage.stats index b99dacf6dff..717ab40a58e 100644 --- a/coverage.stats +++ b/coverage.stats @@ -1 +1 @@ -95.0 \ No newline at end of file +95.0 diff --git a/src/Solana/AccountMeta.h b/src/Solana/AccountMeta.h new file mode 100644 index 00000000000..c5545603fde --- /dev/null +++ b/src/Solana/AccountMeta.h @@ -0,0 +1,20 @@ +// Copyright © 2017-2023 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 "Solana/Address.h" + +namespace TW::Solana { + +struct AccountMeta { + Address account; + bool isSigner; + bool isReadOnly; + AccountMeta(const Address& address, bool isSigner, bool isReadOnly): account(address), isSigner(isSigner), isReadOnly(isReadOnly) {} +}; + +} diff --git a/src/Solana/Address.h b/src/Solana/Address.h index eef248645d8..97b90f3dfd0 100644 --- a/src/Solana/Address.h +++ b/src/Solana/Address.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/src/Solana/AddressLookupTable.h b/src/Solana/AddressLookupTable.h new file mode 100644 index 00000000000..2330781f6a9 --- /dev/null +++ b/src/Solana/AddressLookupTable.h @@ -0,0 +1,20 @@ +// Copyright © 2017-2023 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 + +namespace TW::Solana { + +struct AddressLookupTable { + Solana::Address key; + std::vector addresses; +}; + +using MessageAddressTableLookup = std::vector; + +} diff --git a/src/Solana/CompiledInstruction.cpp b/src/Solana/CompiledInstruction.cpp new file mode 100644 index 00000000000..f2e368edd1a --- /dev/null +++ b/src/Solana/CompiledInstruction.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 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 "Solana/CompiledInstruction.h" + +namespace TW::Solana { + +uint8_t CompiledInstruction::findAccount(const Address& address) { + auto it = std::find(addresses.begin(), addresses.end(), address); + if (it == addresses.end()) { + throw std::invalid_argument("address not found"); + } + assert(it != addresses.end()); + auto dist = std::distance(addresses.begin(), it); + assert(dist < 256); + return (uint8_t)dist; +} + +} diff --git a/src/Solana/CompiledInstruction.h b/src/Solana/CompiledInstruction.h new file mode 100644 index 00000000000..1507886f185 --- /dev/null +++ b/src/Solana/CompiledInstruction.h @@ -0,0 +1,38 @@ +// Copyright © 2017-2023 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 "Solana/Instruction.h" + +namespace TW::Solana { + +// A compiled instruction +struct CompiledInstruction { + // Index into the transaction keys array indicating the program account that executes this instruction + uint8_t programIdIndex; + // Ordered indices into the transaction keys array indicating which accounts + // to pass to the program + std::vector accounts; + // The program input data + Data data; + + // Reference to the address vector + const std::vector

& addresses; + + /// Supplied address vector is expected to contain all addresses and programId from the instruction; they are replaced by index into the address vector. + CompiledInstruction(const Instruction& instruction, const std::vector
& addresses): addresses(addresses) { + programIdIndex = findAccount(instruction.programId); + for (auto& account: instruction.accounts) { + accounts.push_back(findAccount(account.account)); + } + data = instruction.data; + } + + uint8_t findAccount(const Address& address); +}; + +} diff --git a/src/Solana/Constants.h b/src/Solana/Constants.h new file mode 100644 index 00000000000..588be1ec89e --- /dev/null +++ b/src/Solana/Constants.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2023 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 + +namespace TW::Solana { +// https://docs.solana.com/developing/programming-model/transactions + +static const std::string SYSTEM_PROGRAM_ID_ADDRESS = "11111111111111111111111111111111"; +static const std::string STAKE_PROGRAM_ID_ADDRESS = "Stake11111111111111111111111111111111111111"; +static const std::string TOKEN_PROGRAM_ID_ADDRESS = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; +static const std::string ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"; +static const std::string SYSVAR_RENT_ID_ADDRESS = "SysvarRent111111111111111111111111111111111"; +static const std::string SYSVAR_CLOCK_ID_ADDRESS = "SysvarC1ock11111111111111111111111111111111"; +static const std::string STAKE_CONFIG_ID_ADDRESS = "StakeConfig11111111111111111111111111111111"; +static const std::string NULL_ID_ADDRESS = "11111111111111111111111111111111"; +static const std::string SYSVAR_STAKE_HISTORY_ID_ADDRESS = "SysvarStakeHistory1111111111111111111111111"; +static const std::string MEMO_PROGRAM_ID_ADDRESS = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"; +// https://github.com/solana-labs/solana/blob/master/sdk/program/src/message/versions/mod.rs#L24 +static const std::uint8_t MESSAGE_VERSION_PREFIX{0x80}; + +} diff --git a/src/Solana/Encoding.h b/src/Solana/Encoding.h new file mode 100644 index 00000000000..feb2f6fa391 --- /dev/null +++ b/src/Solana/Encoding.h @@ -0,0 +1,31 @@ +// Copyright © 2017-2023 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" + +namespace TW::Solana { + +template +Data shortVecLength(std::vector vec) { + auto bytes = Data(); + auto remLen = vec.size(); + while (true) { + uint8_t elem = remLen & 0x7f; + remLen >>= 7; + if (remLen == 0) { + bytes.push_back(elem); + break; + } else { + elem |= 0x80; + bytes.push_back(elem); + } + } + return bytes; +} + +} diff --git a/src/Solana/Instruction.h b/src/Solana/Instruction.h new file mode 100644 index 00000000000..7c9dd24cc05 --- /dev/null +++ b/src/Solana/Instruction.h @@ -0,0 +1,136 @@ +// Copyright © 2017-2023 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 "BinaryCoding.h" +#include "Solana/Address.h" +#include "Solana/AccountMeta.h" +#include "Solana/Constants.h" + +namespace TW::Solana { + +// System instruction types +enum SystemInstruction { + CreateAccount, + Assign, + Transfer, + CreateAccountWithSeed +}; + +// Stake instruction types +enum StakeInstruction { + Initialize = 0, + DelegateStake = 2, + Withdraw = 4, + Deactivate = 5, +}; + +// Token instruction types +enum TokenInstruction { + CreateTokenAccount = 1, + //SetAuthority = 6, + TokenTransfer = 12, +}; + +// An instruction to execute a program +struct Instruction { + // Index into the transaction keys array indicating the program account that + // executes this instruction + Address programId; + // Ordered indices into the transaction keys array indicating which accounts + // to pass to the program + std::vector accounts; + // The program input data + Data data; + + Instruction(const Address& programId, const std::vector& accounts, const Data& data) + : programId(programId), accounts(accounts), data(data) {} + + // This creator creates a default System Transfer instruction + static Instruction createTransfer(const std::vector& accounts, uint64_t value) { + const SystemInstruction type = Transfer; + auto data = Data(); + encode32LE(static_cast(type), data); + encode64LE(static_cast(value), data); + + return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); + } + + static Instruction createAccountWithSeed(const std::vector& accounts, uint64_t value, uint64_t space, const Address& programId, + const Address& voteAddress, uint64_t seedLength, const Address& signer) { + const SystemInstruction type = CreateAccountWithSeed; + auto data = Data(); + std::string seed = voteAddress.string(); + Data vecSeed(seed.begin(), seed.end()); + vecSeed.resize(static_cast(seedLength)); + encode32LE(static_cast(type), data); + append(data, signer.vector()); + encode64LE(static_cast(seedLength), data); + append(data, vecSeed); + encode64LE(static_cast(value), data); + encode64LE(static_cast(space), data); + append(data, programId.vector()); + + return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); + } + + // creates an Initialize Stake instruction + static Instruction createStakeInitialize(const std::vector& accounts, const Address& signer) { + const StakeInstruction type = Initialize; + auto data = Data(); + encode32LE(static_cast(type), data); + append(data, signer.vector()); + append(data, signer.vector()); + auto lockup = Data(48); + append(data, lockup); + + return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); + } + + // creates a Withdraw Stake instruction + static Instruction createStakeWithdraw(const std::vector& accounts, uint64_t value) { + const StakeInstruction type = Withdraw; + auto data = Data(); + encode32LE(static_cast(type), data); + encode64LE(static_cast(value), data); + + return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); + } + + // creates a Stake instruction + static Instruction createStake(StakeInstruction type, const std::vector& accounts) { + auto data = Data(); + encode32LE(static_cast(type), data); + + return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); + } + + // creates a createAccount token instruction. + static Instruction createTokenCreateAccount(const std::vector& accounts) { + auto data = Data(); + return Instruction(Address(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS), accounts, data); + } + + // creates a transfer token instruction. + static Instruction createTokenTransfer(const std::vector& accounts, uint64_t value, uint8_t decimals) { + const TokenInstruction type = TokenTransfer; + auto data = Data(); + data.push_back(static_cast(type)); + encode64LE(value, data); + data.push_back(static_cast(decimals)); + + return Instruction(Address(TOKEN_PROGRAM_ID_ADDRESS), accounts, data); + } + + static Instruction createMemo(std::string memo) { + auto data = TW::data(memo); + std::vector accounts; // empty + return Instruction(Address(MEMO_PROGRAM_ID_ADDRESS), accounts, data); + } +}; + +} diff --git a/src/Solana/LegacyMessage.cpp b/src/Solana/LegacyMessage.cpp new file mode 100644 index 00000000000..42f411578c9 --- /dev/null +++ b/src/Solana/LegacyMessage.cpp @@ -0,0 +1,99 @@ +// Copyright © 2017-2023 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 "Solana/LegacyMessage.h" +#include "Solana/Encoding.h" + +namespace TW::Solana { +void LegacyMessage::addAccount(const AccountMeta& account) { + bool inSigned = (std::find(signedAccounts.begin(), signedAccounts.end(), account.account) != signedAccounts.end()); + bool inUnsigned = (std::find(unsignedAccounts.begin(), unsignedAccounts.end(), account.account) != unsignedAccounts.end()); + bool inReadOnly = (std::find(readOnlyAccounts.begin(), readOnlyAccounts.end(), account.account) != readOnlyAccounts.end()); + if (account.isSigner) { + if (!inSigned) { + signedAccounts.push_back(account.account); + } + } else if (!account.isReadOnly) { + if (!inSigned && !inUnsigned) { + unsignedAccounts.push_back(account.account); + } + } else { + if (!inSigned && !inUnsigned && !inReadOnly) { + readOnlyAccounts.push_back(account.account); + } + } +} + +void LegacyMessage::addAccountKeys(const Address& account) { + if (std::find(accountKeys.begin(), accountKeys.end(), account) == accountKeys.end()) { + accountKeys.push_back(account); + } +} + +Data LegacyMessage::serialize() const { + Data buffer; + + buffer.push_back(this->header.numRequiredSignatures); + buffer.push_back(this->header.numCreditOnlySignedAccounts); + buffer.push_back(this->header.numCreditOnlyUnsignedAccounts); + append(buffer, shortVecLength
(this->accountKeys)); + for (auto account_key : this->accountKeys) { + Data account_key_vec(account_key.bytes.begin(), account_key.bytes.end()); + append(buffer, account_key_vec); + } + append(buffer, mRecentBlockHash); + + // apppend compiled instructions + append(buffer, shortVecLength(compiledInstructions)); + for (auto instruction : compiledInstructions) { + buffer.push_back(instruction.programIdIndex); + append(buffer, shortVecLength(instruction.accounts)); + append(buffer, instruction.accounts); + append(buffer, shortVecLength(instruction.data)); + append(buffer, instruction.data); + } + + return buffer; +} + +void LegacyMessage::compileAccounts() { + for (auto& instr : instructions) { + for (auto& address : instr.accounts) { + addAccount(address); + } + } + // add programIds (read-only, at end) + for (auto& instr : instructions) { + addAccount(AccountMeta{instr.programId, false, true}); + } + + header = MessageHeader{ + (uint8_t)signedAccounts.size(), + 0, + (uint8_t)readOnlyAccounts.size()}; + + // merge the three buckets + accountKeys.clear(); + for (auto& a : signedAccounts) { + addAccountKeys(a); + } + for (auto& a : unsignedAccounts) { + addAccountKeys(a); + } + for (auto& a : readOnlyAccounts) { + addAccountKeys(a); + } + + compileInstructions(); +} + +void LegacyMessage::compileInstructions() { + compiledInstructions.clear(); + for (auto instruction : instructions) { + compiledInstructions.emplace_back(CompiledInstruction(instruction, accountKeys)); + } +} +} diff --git a/src/Solana/LegacyMessage.h b/src/Solana/LegacyMessage.h new file mode 100644 index 00000000000..f6dca77a5ef --- /dev/null +++ b/src/Solana/LegacyMessage.h @@ -0,0 +1,253 @@ +// Copyright © 2017-2023 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 "Solana/MessageHeader.h" +#include "Solana/Address.h" +#include "Solana/Instruction.h" +#include "Solana/CompiledInstruction.h" +#include "Solana/Constants.h" + +#include + +namespace TW::Solana { + +class LegacyMessage { +public: + // The message header, identifying signed and credit-only `accountKeys` + MessageHeader header; + // All the account keys used by this transaction + std::vector
accountKeys; + // The id of a recent ledger entry. + Data mRecentBlockHash; + // Programs that will be executed in sequence and committed in one atomic + // transaction if all succeed. + std::vector instructions; + + // three buckets of different account types + std::vector
signedAccounts; + std::vector
unsignedAccounts; + std::vector
readOnlyAccounts; + std::vector compiledInstructions; + + LegacyMessage() + : mRecentBlockHash(Base58::bitcoin.decode(NULL_ID_ADDRESS)){}; + + LegacyMessage(Data recentBlockHash, const std::vector& instructions) + : mRecentBlockHash(recentBlockHash) + , instructions(instructions) { + compileAccounts(); + } + + // add an acount, to the corresponding bucket + void addAccount(const AccountMeta& account); + // add an account to accountKeys if not yet present + void addAccountKeys(const Address& account); + // compile the single accounts lists from the buckets + void compileAccounts(); + // compile the instructions; replace instruction accounts with indices + void compileInstructions(); + + // Serialize to msg data + Data serialize() const; + + static void appendReferences(std::vector& accountMetas, const std::vector
& references) { + for (auto&& reference : references) { + accountMetas.emplace_back(reference, false, true); + } + } + + // This constructor creates a default single-signer Transfer message + static LegacyMessage createTransfer(const Address& from, const Address& to, uint64_t value, Data mRecentBlockHash, + std::string memo = "", std::vector
references = {}) { + std::vector instructions; + if (memo.length() > 0) { + // Optional memo. Order: before transfer, as per documentation. + instructions.push_back(Instruction::createMemo(memo)); + } + std::vector accountMetas = { + AccountMeta(from, true, false), + AccountMeta(to, false, false), + }; + appendReferences(accountMetas, references); + instructions.push_back(Instruction::createTransfer(accountMetas, value)); + return LegacyMessage(mRecentBlockHash, instructions); + } + + // This constructor creates a create_account_with_seed_and_delegate_stake message + // see delegate_stake() solana/programs/stake/src/stake_instruction.rs + static LegacyMessage createStake(const Address& signer, const Address& stakeAddress, const Address& voteAddress, uint64_t value, Data mRecentBlockHash) { + auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); + auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); + auto stakeConfigId = Address(STAKE_CONFIG_ID_ADDRESS); + auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); + auto stakeProgramId = Address(STAKE_PROGRAM_ID_ADDRESS); + std::vector instructions; + instructions.reserve(3); + // create_account_with_seed instruction + Address seed = Address(mRecentBlockHash); + auto createAccountInstruction = Instruction::createAccountWithSeed(std::vector{ + AccountMeta(signer, true, true), + AccountMeta(stakeAddress, false, false), + AccountMeta(signer, true, true), + }, + value, 200, stakeProgramId, seed, 32, signer); + instructions.push_back(createAccountInstruction); + // initialize instruction + auto initializeInstruction = Instruction::createStakeInitialize(std::vector{ + AccountMeta(stakeAddress, false, false), + AccountMeta(sysvarRentId, false, true)}, + signer); + instructions.push_back(initializeInstruction); + // delegate_stake instruction + auto delegateInstruction = Instruction::createStake(DelegateStake, + std::vector{ + AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Initialized stake account to be delegated + AccountMeta(voteAddress, false, true), // 1. `[]` Vote account to which this stake will be delegated + AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar + AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history + AccountMeta(stakeConfigId, false, true), // 4. `[]` Address of config account that carries stake config + AccountMeta(signer, true, true), // 5. `[SIGNER]` Stake authority + }); + instructions.push_back(delegateInstruction); + return LegacyMessage(mRecentBlockHash, instructions); + } + + // This constructor creates a deactivate_stake message + static LegacyMessage createStakeDeactivate(const Address& signer, const Address& stakeAddress, Data mRecentBlockHash) { + auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); + auto instruction = Instruction::createStake(Deactivate, std::vector{ + AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Delegated stake account + AccountMeta(sysvarClockId, false, true), // 1. `[]` Clock sysvar + AccountMeta(signer, true, false), // 2. `[SIGNER]` Stake authority + }); + return LegacyMessage(mRecentBlockHash, {instruction}); + } + + // This constructor creates a deactivate_stake message with multiple stake accounts + static LegacyMessage createStakeDeactivateAll(const Address& signer, const std::vector
& stakeAddresses, Data mRecentBlockHash) { + auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); + std::vector instructions; + for (auto& address : stakeAddresses) { + auto instruction = Instruction::createStake(Deactivate, std::vector{ + AccountMeta(address, false, false), // 0. `[WRITE]` Delegated stake account + AccountMeta(sysvarClockId, false, true), // 1. `[]` Clock sysvar + AccountMeta(signer, true, false), // 2. `[SIGNER]` Stake authority + }); + instructions.push_back(instruction); + } + return LegacyMessage(mRecentBlockHash, instructions); + } + + // This constructor creates a withdraw message, with the signer as the default recipient + static LegacyMessage createStakeWithdraw(const Address& signer, const Address& stakeAddress, uint64_t value, Data mRecentBlockHash) { + auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); + auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); + auto instruction = Instruction::createStakeWithdraw(std::vector{ + AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Stake account from which to withdraw + AccountMeta(signer, false, false), // 1. `[WRITE]` Recipient account + AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar + AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history + AccountMeta(signer, true, false), // 4. `[SIGNER]` Withdraw authority + }, + value); + return LegacyMessage(mRecentBlockHash, {instruction}); + } + + // This constructor creates a withdraw message, with multiple stake accounts + static LegacyMessage createStakeWithdrawAll(const Address& signer, const std::vector>& stakes, Data mRecentBlockHash) { + auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); + auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); + std::vector instructions; + for (auto& stake : stakes) { + auto instruction = Instruction::createStakeWithdraw(std::vector{ + AccountMeta(stake.first, false, false), // 0. `[WRITE]` Stake account from which to withdraw + AccountMeta(signer, false, false), // 1. `[WRITE]` Recipient account + AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar + AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history + AccountMeta(signer, true, false), // 4. `[SIGNER]` Withdraw authority + }, + stake.second); + instructions.push_back(instruction); + } + return LegacyMessage(mRecentBlockHash, instructions); + } + + // This constructor creates a createAccount token message + // see create_associated_token_account() solana-program-library/associated-token-account/program/src/lib.rs + static LegacyMessage createTokenCreateAccount(const Address& signer, const Address& otherMainAccount, const Address& tokenMintAddress, const Address& tokenAddress, Data mRecentBlockHash) { + auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); + auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); + auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); + auto instruction = Instruction::createTokenCreateAccount(std::vector{ + AccountMeta(signer, true, false), // fundingAddress, + AccountMeta(tokenAddress, false, false), + AccountMeta(otherMainAccount, false, true), + AccountMeta(tokenMintAddress, false, true), + AccountMeta(systemProgramId, false, true), + AccountMeta(tokenProgramId, false, true), + AccountMeta(sysvarRentId, false, true), + }); + return LegacyMessage(mRecentBlockHash, {instruction}); + } + + // This constructor creates a transfer token message. + // see transfer_checked() solana-program-library/token/program/src/instruction.rs + static LegacyMessage createTokenTransfer(const Address& signer, const Address& tokenMintAddress, + const Address& senderTokenAddress, const Address& recipientTokenAddress, uint64_t amount, uint8_t decimals, Data mRecentBlockHash, + std::string memo = "", std::vector
references = {}) { + std::vector instructions; + if (memo.length() > 0) { + // Optional memo. Order: before transfer, as per documentation. + instructions.push_back(Instruction::createMemo(memo)); + } + std::vector accountMetas = { + AccountMeta(senderTokenAddress, false, false), + AccountMeta(tokenMintAddress, false, true), + AccountMeta(recipientTokenAddress, false, false), + AccountMeta(signer, true, false), + }; + appendReferences(accountMetas, references); + instructions.push_back(Instruction::createTokenTransfer(accountMetas, amount, decimals)); + return LegacyMessage(mRecentBlockHash, instructions); + } + + // This constructor creates a createAndTransferToken message, combining createAccount and transfer. + static LegacyMessage createTokenCreateAndTransfer(const Address& signer, const Address& recipientMainAddress, const Address& tokenMintAddress, + const Address& recipientTokenAddress, const Address& senderTokenAddress, uint64_t amount, uint8_t decimals, Data mRecentBlockHash, + std::string memo = "", std::vector
references = {}) { + const auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); + const auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); + const auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); + std::vector instructions; + instructions.reserve(3); + instructions.emplace_back(Instruction::createTokenCreateAccount(std::vector{ + AccountMeta(signer, true, false), // fundingAddress, + AccountMeta(recipientTokenAddress, false, false), + AccountMeta(recipientMainAddress, false, true), + AccountMeta(tokenMintAddress, false, true), + AccountMeta(systemProgramId, false, true), + AccountMeta(tokenProgramId, false, true), + AccountMeta(sysvarRentId, false, true), + })); + if (memo.length() > 0) { + // Optional memo. Order: before transfer, as per documentation. + instructions.emplace_back(Instruction::createMemo(memo)); + } + std::vector accountMetas = { + AccountMeta(senderTokenAddress, false, false), + AccountMeta(tokenMintAddress, false, true), + AccountMeta(recipientTokenAddress, false, false), + AccountMeta(signer, true, false), + }; + appendReferences(accountMetas, references); + instructions.push_back(Instruction::createTokenTransfer(accountMetas, amount, decimals)); + return LegacyMessage(mRecentBlockHash, instructions); + } +}; + +} diff --git a/src/Solana/MessageHeader.h b/src/Solana/MessageHeader.h new file mode 100644 index 00000000000..bb06ce56076 --- /dev/null +++ b/src/Solana/MessageHeader.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 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 // << std::uint8_t + +struct MessageHeader { + // The number of signatures required for this message to be considered + // valid. The signatures must match the first `numRequiredSignatures` of + // `accountKeys`. + uint8_t numRequiredSignatures = 0; + // The last numCreditOnlySignedAccounts of the signed keys are + // credit-only accounts. + uint8_t numCreditOnlySignedAccounts = 0; + // The last numCreditOnlyUnsignedAccounts of the unsigned keys are + // credit-only accounts. + uint8_t numCreditOnlyUnsignedAccounts = 0; +}; diff --git a/src/Solana/Program.cpp b/src/Solana/Program.cpp index bac0340caa8..d5023c3a66a 100644 --- a/src/Solana/Program.cpp +++ b/src/Solana/Program.cpp @@ -25,9 +25,9 @@ Address StakeProgram::addressFromValidatorSeed(const Address& fromAddress, const return Address(hash); } -Address StakeProgram::addressFromRecentBlockhash(const Address& fromAddress, const Hash& recentBlockhash, const Address& programId) { +Address StakeProgram::addressFromRecentBlockhash(const Address& fromAddress, const Data& recentBlockhash, const Address& programId) { Data extended = fromAddress.vector(); - std::string seed = recentBlockhash.encoded(); + std::string seed = Base58::bitcoin.encode(recentBlockhash); Data vecSeed(seed.begin(), seed.end()); vecSeed.resize(32); Data additional = programId.vector(); diff --git a/src/Solana/Program.h b/src/Solana/Program.h index 4214bb98355..e2880c3c173 100644 --- a/src/Solana/Program.h +++ b/src/Solana/Program.h @@ -19,7 +19,7 @@ class StakeProgram { const Address& validatorAddress, const Address& programId); - static Address addressFromRecentBlockhash(const Address& fromAddress, const Hash& recentBlockhash, const Address& programId); + static Address addressFromRecentBlockhash(const Address& fromAddress, const Data& recentBlockhash, const Address& programId); }; diff --git a/src/Solana/Signature.cpp b/src/Solana/Signature.cpp new file mode 100644 index 00000000000..e054ac0f8c2 --- /dev/null +++ b/src/Solana/Signature.cpp @@ -0,0 +1,13 @@ +// Copyright © 2017-2023 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 "Solana/Signature.h" + +namespace TW::Solana { + bool Signature::operator==(const Signature& v) const { + return bytes == v.bytes; + } +} diff --git a/src/Solana/Signature.h b/src/Solana/Signature.h new file mode 100644 index 00000000000..8cb169b6e6a --- /dev/null +++ b/src/Solana/Signature.h @@ -0,0 +1,30 @@ +// Copyright © 2017-2023 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 +#include + +#include "Base58.h" + +namespace TW::Solana { +class Signature { +public: + static const size_t size = 64; + /// Signature data + std::array bytes; + + Signature(const std::string& string) { + const auto data = Base58::bitcoin.decode(string); + std::copy(data.begin(), data.end(), this->bytes.begin()); + } + Signature(const std::array& bytes) { this->bytes = bytes; } + Signature(const Data& bytes) { std::copy(bytes.begin(), bytes.end(), this->bytes.begin()); } + + bool operator==(const Signature& v) const; +}; +} diff --git a/src/Solana/Signer.cpp b/src/Solana/Signer.cpp index e14351939fd..e427bd76401 100644 --- a/src/Solana/Signer.cpp +++ b/src/Solana/Signer.cpp @@ -7,6 +7,8 @@ #include "Signer.h" #include "Address.h" #include "Program.h" +#include "Solana/Encoding.h" +#include "Solana/VersionedTransaction.h" #include @@ -14,7 +16,7 @@ namespace TW::Solana { -void Signer::sign(const std::vector& privateKeys, Transaction& transaction) { +void Signer::sign(const std::vector& privateKeys, VersionedTransaction& transaction) { for (auto privateKey : privateKeys) { auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); auto index = transaction.getAccountIndex(address); @@ -36,15 +38,15 @@ std::vector
convertReferences(const google::protobuf::RepeatedPtrField< } Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { - auto blockhash = Solana::Hash(input.recent_blockhash()); + auto blockhash = Base58::bitcoin.decode(input.recent_blockhash()); auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - Message message; + LegacyMessage message; std::vector signerKeys; switch (input.transaction_type_case()) { case Proto::SigningInput::TransactionTypeCase::kTransferTransaction: { auto protoMessage = input.transfer_transaction(); - message = Message::createTransfer( + message = LegacyMessage::createTransfer( /* from */ Address(key.getPublicKey(TWPublicKeyTypeED25519)), /* to */ Address(protoMessage.recipient()), /* value */ protoMessage.value(), @@ -67,7 +69,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { // stake address specified, use it stakeAddress = Address(protoMessage.stake_account()); } - message = Message::createStake( + message = LegacyMessage::createStake( /* signer */ userAddress, /* stakeAddress */ stakeAddress.value(), /* voteAddress */ validatorAddress, @@ -80,7 +82,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto protoMessage = input.deactivate_stake_transaction(); auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); auto stakeAddress = Address(protoMessage.stake_account()); - message = Message::createStakeDeactivate( + message = LegacyMessage::createStakeDeactivate( /* signer */ userAddress, /* stakeAddress */ stakeAddress, /* recent_blockhash */ blockhash); @@ -94,7 +96,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { for (auto i = 0; i < protoMessage.stake_accounts_size(); ++i) { addresses.emplace_back(Address(protoMessage.stake_accounts(i))); } - message = Message::createStakeDeactivateAll(userAddress, addresses, blockhash); + message = LegacyMessage::createStakeDeactivateAll(userAddress, addresses, blockhash); signerKeys.push_back(key); } break; @@ -102,7 +104,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto protoMessage = input.withdraw_transaction(); auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); auto stakeAddress = Address(protoMessage.stake_account()); - message = Message::createStakeWithdraw( + message = LegacyMessage::createStakeWithdraw( /* signer */ userAddress, /* stakeAddress */ stakeAddress, /* value */ protoMessage.value(), @@ -119,7 +121,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { Address(protoMessage.stake_accounts(i).stake_account()), protoMessage.stake_accounts(i).value())); } - message = Message::createStakeWithdrawAll(userAddress, stakes, blockhash); + message = LegacyMessage::createStakeWithdrawAll(userAddress, stakes, blockhash); signerKeys.push_back(key); } break; @@ -129,7 +131,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto mainAddress = Address(protoMessage.main_address()); auto tokenMintAddress = Address(protoMessage.token_mint_address()); auto tokenAddress = Address(protoMessage.token_address()); - message = Message::createTokenCreateAccount(userAddress, mainAddress, tokenMintAddress, tokenAddress, blockhash); + message = LegacyMessage::createTokenCreateAccount(userAddress, mainAddress, tokenMintAddress, tokenAddress, blockhash); signerKeys.push_back(key); } break; @@ -142,7 +144,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto amount = protoMessage.amount(); auto decimals = static_cast(protoMessage.decimals()); const auto memo = protoMessage.memo(); - message = Message::createTokenTransfer(userAddress, tokenMintAddress, senderTokenAddress, recipientTokenAddress, amount, decimals, blockhash, + message = LegacyMessage::createTokenTransfer(userAddress, tokenMintAddress, senderTokenAddress, recipientTokenAddress, amount, decimals, blockhash, memo, convertReferences(protoMessage.references())); signerKeys.push_back(key); } break; @@ -157,7 +159,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto amount = protoMessage.amount(); auto decimals = static_cast(protoMessage.decimals()); const auto memo = protoMessage.memo(); - message = Message::createTokenCreateAndTransfer(userAddress, recipientMainAddress, tokenMintAddress, recipientTokenAddress, senderTokenAddress, amount, decimals, blockhash, + message = LegacyMessage::createTokenCreateAndTransfer(userAddress, recipientMainAddress, tokenMintAddress, recipientTokenAddress, senderTokenAddress, amount, decimals, blockhash, memo, convertReferences(protoMessage.references())); signerKeys.push_back(key); } break; @@ -165,7 +167,11 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { default: assert(input.transaction_type_case() != Proto::SigningInput::TransactionTypeCase::TRANSACTION_TYPE_NOT_SET); } - auto transaction = Transaction(message); + auto msg = VersionedMessage(message); + if (input.v0_msg()) { + msg = VersionedMessage(V0Message{.msg = message}); + } + auto transaction = VersionedTransaction(msg); sign(signerKeys, transaction); @@ -180,8 +186,8 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { } void Signer::signUpdateBlockhash(const std::vector& privateKeys, - Transaction& transaction, Solana::Hash& recentBlockhash) { - transaction.message.recentBlockhash = recentBlockhash; + VersionedTransaction& transaction, Data& recentBlockhash) { + updateRecentHash(transaction.message, recentBlockhash); Signer::sign(privateKeys, transaction); } diff --git a/src/Solana/Signer.h b/src/Solana/Signer.h index 46bed571ffb..d89d4656a34 100644 --- a/src/Solana/Signer.h +++ b/src/Solana/Signer.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,7 +6,7 @@ #pragma once -#include "Transaction.h" +#include "VersionedTransaction.h" #include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" @@ -18,13 +18,13 @@ namespace TW::Solana { class Signer { public: /// Signs the given transaction. - static void sign(const std::vector& privateKeys, Transaction& transaction); + static void sign(const std::vector& privateKeys, VersionedTransaction& transaction); /// Signs a json Proto::SigningInput with private key static std::string signJSON(const std::string& json, const Data& key); static void signUpdateBlockhash(const std::vector& privateKeys, - Transaction& transaction, Solana::Hash& recentBlockhash); + VersionedTransaction& transaction, Data& recentBlockhash); static Data signRawMessage(const std::vector& privateKeys, const Data messageData); static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; diff --git a/src/Solana/Transaction.cpp b/src/Solana/Transaction.cpp index ac345134dc6..172779dee30 100644 --- a/src/Solana/Transaction.cpp +++ b/src/Solana/Transaction.cpp @@ -1,10 +1,11 @@ -// Copyright © 2017-2022 Trust Wallet. +// Copyright © 2017-2023 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 "Solana/Encoding.h" #include "Hash.h" #include "Signer.h" @@ -13,80 +14,6 @@ namespace TW::Solana { -uint8_t CompiledInstruction::findAccount(const Address& address) { - auto it = std::find(addresses.begin(), addresses.end(), address); - if (it == addresses.end()) { - throw std::invalid_argument("address not found"); - } - assert(it != addresses.end()); - auto dist = std::distance(addresses.begin(), it); - assert(dist < 256); - return (uint8_t)dist; -} - -void Message::addAccount(const AccountMeta& account) { - bool inSigned = (std::find(signedAccounts.begin(), signedAccounts.end(), account.account) != signedAccounts.end()); - bool inUnsigned = (std::find(unsignedAccounts.begin(), unsignedAccounts.end(), account.account) != unsignedAccounts.end()); - bool inReadOnly = (std::find(readOnlyAccounts.begin(), readOnlyAccounts.end(), account.account) != readOnlyAccounts.end()); - if (account.isSigner) { - if (!inSigned) { - signedAccounts.push_back(account.account); - } - } else if (!account.isReadOnly) { - if (!inSigned && !inUnsigned) { - unsignedAccounts.push_back(account.account); - } - } else { - if (!inSigned && !inUnsigned && !inReadOnly) { - readOnlyAccounts.push_back(account.account); - } - } -} - -void Message::addAccountKeys(const Address& account) { - if (std::find(accountKeys.begin(), accountKeys.end(), account) == accountKeys.end()) { - accountKeys.push_back(account); - } -} - -void Message::compileAccounts() { - for (auto& instr : instructions) { - for (auto& address : instr.accounts) { - addAccount(address); - } - } - // add programIds (read-only, at end) - for (auto& instr : instructions) { - addAccount(AccountMeta{instr.programId, false, true}); - } - - header = MessageHeader{ - (uint8_t)signedAccounts.size(), - 0, - (uint8_t)readOnlyAccounts.size()}; - - // merge the three buckets - accountKeys.clear(); - for (auto& a : signedAccounts) { - addAccountKeys(a); - } - for (auto& a : unsignedAccounts) { - addAccountKeys(a); - } - for (auto& a : readOnlyAccounts) { - addAccountKeys(a); - } - - compileInstructions(); -} - -void Message::compileInstructions() { - compiledInstructions.clear(); - for (auto instruction : instructions) { - compiledInstructions.emplace_back(CompiledInstruction(instruction, accountKeys)); - } -} - std::string Transaction::serialize() const { Data buffer; @@ -101,31 +28,7 @@ std::string Transaction::serialize() const { } Data Transaction::messageData() const { - Data buffer; - - buffer.push_back(this->message.header.numRequiredSignatures); - buffer.push_back(this->message.header.numCreditOnlySignedAccounts); - buffer.push_back(this->message.header.numCreditOnlyUnsignedAccounts); - append(buffer, shortVecLength
(this->message.accountKeys)); - for (auto account_key : this->message.accountKeys) { - Data account_key_vec(account_key.bytes.begin(), account_key.bytes.end()); - append(buffer, account_key_vec); - } - Data recentBlockhash(this->message.recentBlockhash.bytes.begin(), - this->message.recentBlockhash.bytes.end()); - append(buffer, recentBlockhash); - - // apppend compiled instructions - append(buffer, shortVecLength(message.compiledInstructions)); - for (auto instruction : message.compiledInstructions) { - buffer.push_back(instruction.programIdIndex); - append(buffer, shortVecLength(instruction.accounts)); - append(buffer, instruction.accounts); - append(buffer, shortVecLength(instruction.data)); - append(buffer, instruction.data); - } - - return buffer; + return this->message.serialize(); } uint8_t Transaction::getAccountIndex(Address publicKey) { @@ -137,8 +40,4 @@ uint8_t Transaction::getAccountIndex(Address publicKey) { return (uint8_t)std::distance(this->message.accountKeys.begin(), item); } -bool Signature::operator==(const Signature& v) const { - return bytes == v.bytes; -} - } // namespace TW::Solana diff --git a/src/Solana/Transaction.h b/src/Solana/Transaction.h index 8ea3e6c5fca..65d24a3bfb0 100644 --- a/src/Solana/Transaction.h +++ b/src/Solana/Transaction.h @@ -6,493 +6,31 @@ #pragma once -#include "Address.h" -#include "../Base58.h" -#include "../BinaryCoding.h" +#include "Solana/Address.h" +#include "Solana/LegacyMessage.h" +#include "Solana/Signature.h" #include "Data.h" +#include "BinaryCoding.h" #include #include namespace TW::Solana { -// https://docs.solana.com/developing/programming-model/transactions - -const std::string SYSTEM_PROGRAM_ID_ADDRESS = "11111111111111111111111111111111"; -const std::string STAKE_PROGRAM_ID_ADDRESS = "Stake11111111111111111111111111111111111111"; -const std::string TOKEN_PROGRAM_ID_ADDRESS = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; -const std::string ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"; -const std::string SYSVAR_RENT_ID_ADDRESS = "SysvarRent111111111111111111111111111111111"; -const std::string SYSVAR_CLOCK_ID_ADDRESS = "SysvarC1ock11111111111111111111111111111111"; -const std::string STAKE_CONFIG_ID_ADDRESS = "StakeConfig11111111111111111111111111111111"; -const std::string NULL_ID_ADDRESS = "11111111111111111111111111111111"; -const std::string SYSVAR_STAKE_HISTORY_ID_ADDRESS = "SysvarStakeHistory1111111111111111111111111"; -const std::string MEMO_PROGRAM_ID_ADDRESS = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"; - -template -Data shortVecLength(std::vector vec) { - auto bytes = Data(); - auto remLen = vec.size(); - while (true) { - uint8_t elem = remLen & 0x7f; - remLen >>= 7; - if (remLen == 0) { - bytes.push_back(elem); - break; - } else { - elem |= 0x80; - bytes.push_back(elem); - } - } - return bytes; -} - -// System instruction types -enum SystemInstruction { - CreateAccount, - Assign, - Transfer, - CreateAccountWithSeed -}; - -// Stake instruction types -enum StakeInstruction { - Initialize = 0, - DelegateStake = 2, - Withdraw = 4, - Deactivate = 5, -}; - -// Token instruction types -enum TokenInstruction { - CreateTokenAccount = 1, - //SetAuthority = 6, - TokenTransfer = 12, -}; - -enum TokenAuthorityType { - MintTokens = 0, - FreezeAccount = 1, - AccountOwner = 2, - CloseAccount = 3, -}; - -struct AccountMeta { - Address account; - bool isSigner; - bool isReadOnly; - AccountMeta(const Address& address, bool isSigner, bool isReadOnly): account(address), isSigner(isSigner), isReadOnly(isReadOnly) {} -}; - -// An instruction to execute a program -struct Instruction { - // Index into the transaction keys array indicating the program account that - // executes this instruction - Address programId; - // Ordered indices into the transaction keys array indicating which accounts - // to pass to the program - std::vector accounts; - // The program input data - Data data; - - Instruction(const Address& programId, const std::vector& accounts, const Data& data) - : programId(programId), accounts(accounts), data(data) {} - - // This creator creates a default System Transfer instruction - static Instruction createTransfer(const std::vector& accounts, uint64_t value) { - const SystemInstruction type = Transfer; - auto data = Data(); - encode32LE(static_cast(type), data); - encode64LE(static_cast(value), data); - - return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); - } - - static Instruction createAccountWithSeed(const std::vector& accounts, uint64_t value, uint64_t space, const Address& programId, - const Address& voteAddress, uint64_t seedLength, const Address& signer) { - const SystemInstruction type = CreateAccountWithSeed; - auto data = Data(); - std::string seed = voteAddress.string(); - Data vecSeed(seed.begin(), seed.end()); - vecSeed.resize(static_cast(seedLength)); - encode32LE(static_cast(type), data); - append(data, signer.vector()); - encode64LE(static_cast(seedLength), data); - append(data, vecSeed); - encode64LE(static_cast(value), data); - encode64LE(static_cast(space), data); - append(data, programId.vector()); - - return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates an Initialize Stake instruction - static Instruction createStakeInitialize(const std::vector& accounts, const Address& signer) { - const StakeInstruction type = Initialize; - auto data = Data(); - encode32LE(static_cast(type), data); - append(data, signer.vector()); - append(data, signer.vector()); - auto lockup = Data(48); - append(data, lockup); - - return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates a Withdraw Stake instruction - static Instruction createStakeWithdraw(const std::vector& accounts, uint64_t value) { - const StakeInstruction type = Withdraw; - auto data = Data(); - encode32LE(static_cast(type), data); - encode64LE(static_cast(value), data); - - return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates a Stake instruction - static Instruction createStake(StakeInstruction type, const std::vector& accounts) { - auto data = Data(); - encode32LE(static_cast(type), data); - - return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates a createAccount token instruction. - static Instruction createTokenCreateAccount(const std::vector& accounts) { - auto data = Data(); - return Instruction(Address(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates a transfer token instruction. - static Instruction createTokenTransfer(const std::vector& accounts, uint64_t value, uint8_t decimals) { - const TokenInstruction type = TokenTransfer; - auto data = Data(); - data.push_back(static_cast(type)); - encode64LE(value, data); - data.push_back(static_cast(decimals)); - - return Instruction(Address(TOKEN_PROGRAM_ID_ADDRESS), accounts, data); - } - - static Instruction createMemo(std::string memo) { - auto data = TW::data(memo); - std::vector accounts; // empty - return Instruction(Address(MEMO_PROGRAM_ID_ADDRESS), accounts, data); - } -}; - -// A compiled instruction -struct CompiledInstruction { - // Index into the transaction keys array indicating the program account that executes this instruction - uint8_t programIdIndex; - // Ordered indices into the transaction keys array indicating which accounts - // to pass to the program - std::vector accounts; - // The program input data - Data data; - - // Reference to the address vector - const std::vector
& addresses; - - /// Supplied address vector is expected to contain all addresses and programId from the instruction; they are replaced by index into the address vector. - CompiledInstruction(const Instruction& instruction, const std::vector
& addresses): addresses(addresses) { - programIdIndex = findAccount(instruction.programId); - for (auto& account: instruction.accounts) { - accounts.push_back(findAccount(account.account)); - } - data = instruction.data; - } - - uint8_t findAccount(const Address& address); -}; - -class Hash { - public: - static const size_t size = 32; - /// Hash data - std::array bytes; - - Hash(const std::string& string) { - const auto data = Base58::bitcoin.decode(string); - std::copy(data.begin(), data.end(), this->bytes.begin()); - } - - std::string encoded() const { return Base58::bitcoin.encode(bytes); } -}; - -class Signature { - public: - static const size_t size = 64; - /// Signature data - std::array bytes; - - Signature(const std::string& string) { - const auto data = Base58::bitcoin.decode(string); - std::copy(data.begin(), data.end(), this->bytes.begin()); - } - Signature(const std::array& bytes) { this->bytes = bytes; } - Signature(const Data& bytes) { std::copy(bytes.begin(), bytes.end(), this->bytes.begin()); } - - bool operator==(const Signature& v) const; -}; - -struct MessageHeader { - // The number of signatures required for this message to be considered - // valid. The signatures must match the first `numRequiredSignatures` of - // `accountKeys`. - uint8_t numRequiredSignatures = 0; - // The last numCreditOnlySignedAccounts of the signed keys are - // credit-only accounts. - uint8_t numCreditOnlySignedAccounts = 0; - // The last numCreditOnlyUnsignedAccounts of the unsigned keys are - // credit-only accounts. - uint8_t numCreditOnlyUnsignedAccounts = 0; -}; - -class Message { - public: - // The message header, identifying signed and credit-only `accountKeys` - MessageHeader header; - // All the account keys used by this transaction - std::vector
accountKeys; - // The id of a recent ledger entry. - Hash recentBlockhash; - // Programs that will be executed in sequence and committed in one atomic - // transaction if all succeed. - std::vector instructions; - - // three buckets of different account types - std::vector
signedAccounts; - std::vector
unsignedAccounts; - std::vector
readOnlyAccounts; - std::vector compiledInstructions; - - Message() : recentBlockhash(NULL_ID_ADDRESS) {}; - - Message(Hash recentBlockhash, const std::vector& instructions) - : recentBlockhash(recentBlockhash) - , instructions(instructions) { - compileAccounts(); - } - - // add an acount, to the corresponding bucket - void addAccount(const AccountMeta& account); - // add an account to accountKeys if not yet present - void addAccountKeys(const Address& account); - // compile the single accounts lists from the buckets - void compileAccounts(); - // compile the instructions; replace instruction accounts with indices - void compileInstructions(); - - static void appendReferences(std::vector& accountMetas, const std::vector
& references) { - for (auto &&reference: references) { - accountMetas.emplace_back(reference, false, true); - } - } - - // This constructor creates a default single-signer Transfer message - static Message createTransfer(const Address& from, const Address& to, uint64_t value, Hash recentBlockhash, - std::string memo = "", std::vector
references = {} - ) { - std::vector instructions; - if (memo.length() > 0) { - // Optional memo. Order: before transfer, as per documentation. - instructions.push_back(Instruction::createMemo(memo)); - } - std::vector accountMetas = { - AccountMeta(from, true, false), - AccountMeta(to, false, false), - }; - appendReferences(accountMetas, references); - instructions.push_back(Instruction::createTransfer(accountMetas, value)); - return Message(recentBlockhash, instructions); - } - - // This constructor creates a create_account_with_seed_and_delegate_stake message - // see delegate_stake() solana/programs/stake/src/stake_instruction.rs - static Message createStake(const Address& signer, const Address& stakeAddress, const Address& voteAddress, uint64_t value, Hash recentBlockhash) { - auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto stakeConfigId = Address(STAKE_CONFIG_ID_ADDRESS); - auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); - auto stakeProgramId = Address(STAKE_PROGRAM_ID_ADDRESS); - std::vector instructions; - instructions.reserve(3); - // create_account_with_seed instruction - Address seed = Address(data(recentBlockhash.bytes.data(), recentBlockhash.bytes.size())); - auto createAccountInstruction = Instruction::createAccountWithSeed(std::vector{ - AccountMeta(signer, true, true), - AccountMeta(stakeAddress, false, false), - AccountMeta(signer, true, true), - }, value, 200, stakeProgramId, seed, 32, signer); - instructions.push_back(createAccountInstruction); - // initialize instruction - auto initializeInstruction = Instruction::createStakeInitialize(std::vector{ - AccountMeta(stakeAddress, false, false), - AccountMeta(sysvarRentId, false, true) - }, signer); - instructions.push_back(initializeInstruction); - // delegate_stake instruction - auto delegateInstruction = Instruction::createStake(DelegateStake, - std::vector{ - AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Initialized stake account to be delegated - AccountMeta(voteAddress, false, true), // 1. `[]` Vote account to which this stake will be delegated - AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar - AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history - AccountMeta(stakeConfigId, false, true), // 4. `[]` Address of config account that carries stake config - AccountMeta(signer, true, true), // 5. `[SIGNER]` Stake authority - }); - instructions.push_back(delegateInstruction); - return Message(recentBlockhash, instructions); - } - - // This constructor creates a deactivate_stake message - static Message createStakeDeactivate(const Address& signer, const Address& stakeAddress, Hash recentBlockhash) { - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto instruction = Instruction::createStake(Deactivate, std::vector{ - AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Delegated stake account - AccountMeta(sysvarClockId, false, true), // 1. `[]` Clock sysvar - AccountMeta(signer, true, false), // 2. `[SIGNER]` Stake authority - }); - return Message(recentBlockhash, {instruction}); - } - - // This constructor creates a deactivate_stake message with multiple stake accounts - static Message createStakeDeactivateAll(const Address& signer, const std::vector
& stakeAddresses, Hash recentBlockhash) { - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - std::vector instructions; - for(auto& address: stakeAddresses) { - auto instruction = Instruction::createStake(Deactivate, std::vector{ - AccountMeta(address, false, false), // 0. `[WRITE]` Delegated stake account - AccountMeta(sysvarClockId, false, true), // 1. `[]` Clock sysvar - AccountMeta(signer, true, false), // 2. `[SIGNER]` Stake authority - }); - instructions.push_back(instruction); - } - return Message(recentBlockhash, instructions); - } - - // This constructor creates a withdraw message, with the signer as the default recipient - static Message createStakeWithdraw(const Address& signer, const Address& stakeAddress, uint64_t value, Hash recentBlockhash) { - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); - auto instruction = Instruction::createStakeWithdraw(std::vector{ - AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Stake account from which to withdraw - AccountMeta(signer, false, false), // 1. `[WRITE]` Recipient account - AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar - AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history - AccountMeta(signer, true, false), // 4. `[SIGNER]` Withdraw authority - }, value); - return Message(recentBlockhash, {instruction}); - } - - // This constructor creates a withdraw message, with multiple stake accounts - static Message createStakeWithdrawAll(const Address& signer, const std::vector>& stakes, Hash recentBlockhash) { - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); - std::vector instructions; - for(auto& stake: stakes) { - auto instruction = Instruction::createStakeWithdraw(std::vector{ - AccountMeta(stake.first, false, false), // 0. `[WRITE]` Stake account from which to withdraw - AccountMeta(signer, false, false), // 1. `[WRITE]` Recipient account - AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar - AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history - AccountMeta(signer, true, false), // 4. `[SIGNER]` Withdraw authority - }, stake.second); - instructions.push_back(instruction); - } - return Message(recentBlockhash, instructions); - } - - // This constructor creates a createAccount token message - // see create_associated_token_account() solana-program-library/associated-token-account/program/src/lib.rs - static Message createTokenCreateAccount(const Address& signer, const Address& otherMainAccount, const Address& tokenMintAddress, const Address& tokenAddress, Hash recentBlockhash) { - auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); - auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); - auto instruction = Instruction::createTokenCreateAccount(std::vector{ - AccountMeta(signer, true, false), // fundingAddress, - AccountMeta(tokenAddress, false, false), - AccountMeta(otherMainAccount, false, true), - AccountMeta(tokenMintAddress, false, true), - AccountMeta(systemProgramId, false, true), - AccountMeta(tokenProgramId, false, true), - AccountMeta(sysvarRentId, false, true), - }); - return Message(recentBlockhash, {instruction}); - } - - // This constructor creates a transfer token message. - // see transfer_checked() solana-program-library/token/program/src/instruction.rs - static Message createTokenTransfer(const Address& signer, const Address& tokenMintAddress, - const Address& senderTokenAddress, const Address& recipientTokenAddress, uint64_t amount, uint8_t decimals, Hash recentBlockhash, - std::string memo = "", std::vector
references = {} - ) { - std::vector instructions; - if (memo.length() > 0) { - // Optional memo. Order: before transfer, as per documentation. - instructions.push_back(Instruction::createMemo(memo)); - } - std::vector accountMetas = { - AccountMeta(senderTokenAddress, false, false), - AccountMeta(tokenMintAddress, false, true), - AccountMeta(recipientTokenAddress, false, false), - AccountMeta(signer, true, false), - }; - appendReferences(accountMetas, references); - instructions.push_back(Instruction::createTokenTransfer(accountMetas, amount, decimals)); - return Message(recentBlockhash, instructions); - } - - // This constructor creates a createAndTransferToken message, combining createAccount and transfer. - static Message createTokenCreateAndTransfer(const Address& signer, const Address& recipientMainAddress, const Address& tokenMintAddress, - const Address& recipientTokenAddress, const Address& senderTokenAddress, uint64_t amount, uint8_t decimals, Hash recentBlockhash, - std::string memo = "", std::vector
references = {} - ) { - const auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - const auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); - const auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); - std::vector instructions; - instructions.reserve(3); - instructions.emplace_back(Instruction::createTokenCreateAccount(std::vector{ - AccountMeta(signer, true, false), // fundingAddress, - AccountMeta(recipientTokenAddress, false, false), - AccountMeta(recipientMainAddress, false, true), - AccountMeta(tokenMintAddress, false, true), - AccountMeta(systemProgramId, false, true), - AccountMeta(tokenProgramId, false, true), - AccountMeta(sysvarRentId, false, true), - })); - if (memo.length() > 0) { - // Optional memo. Order: before transfer, as per documentation. - instructions.emplace_back(Instruction::createMemo(memo)); - } - std::vector accountMetas = { - AccountMeta(senderTokenAddress, false, false), - AccountMeta(tokenMintAddress, false, true), - AccountMeta(recipientTokenAddress, false, false), - AccountMeta(signer, true, false), - }; - appendReferences(accountMetas, references); - instructions.push_back(Instruction::createTokenTransfer(accountMetas, amount, decimals)); - return Message(recentBlockhash, instructions); - } -}; - class Transaction { public: // Signatures std::vector signatures; // The message to sign - Message message; + LegacyMessage message; - Transaction(const Message& message) : message(message) { + Transaction(const LegacyMessage& message) : message(message) { this->signatures.resize(message.header.numRequiredSignatures, Signature(defaultSignature)); } // Default basic transfer transaction - Transaction(const Address& from, const Address& to, uint64_t value, Hash recentBlockhash, std::string memo = "", std::vector
references = {}) - : message(Message::createTransfer(from, to, value, recentBlockhash, memo, references)) { + Transaction(const Address& from, const Address& to, uint64_t value, Data recentBlockhash, std::string memo = "", std::vector
references = {}) + : message(LegacyMessage::createTransfer(from, to, value, recentBlockhash, memo, references)) { this->signatures.resize(1, Signature(defaultSignature)); } diff --git a/src/Solana/V0Message.h b/src/Solana/V0Message.h new file mode 100644 index 00000000000..a3f7c18a165 --- /dev/null +++ b/src/Solana/V0Message.h @@ -0,0 +1,19 @@ +// Copyright © 2017-2023 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 "Solana/LegacyMessage.h" +#include "Solana/AddressLookupTable.h" + +namespace TW::Solana { + +struct V0Message { + LegacyMessage msg; + MessageAddressTableLookup addressTableLookups; +}; + +} diff --git a/src/Solana/VersionedMessage.cpp b/src/Solana/VersionedMessage.cpp new file mode 100644 index 00000000000..420bd64ea18 --- /dev/null +++ b/src/Solana/VersionedMessage.cpp @@ -0,0 +1,67 @@ +// Copyright © 2017-2023 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 "Solana/VersionedMessage.h" +#include "Solana/Constants.h" +#include "Solana/Encoding.h" + +namespace TW::Solana { + +Data serialize(const VersionedMessage& message) { + auto visit_functor = [](const VersionedMessage& message) -> Data { + if (auto* msg = std::get_if(&message); msg) { + Data out; + append(out, MESSAGE_VERSION_PREFIX); + append(out, msg->msg.serialize()); + append(out, shortVecLength(msg->addressTableLookups)); + return out; + } else if (auto* legacyMsg = std::get_if(&message); legacyMsg) { + return legacyMsg->serialize(); + } else { + return {}; + } + }; + + return std::visit(visit_functor, message); +} + +MessageHeader header(const VersionedMessage& message) { + auto visit_functor = [](const VersionedMessage& message) -> MessageHeader { + if (auto* msg = std::get_if(&message); msg) { + return msg->msg.header; + } else if (auto* legacyMsg = std::get_if(&message); legacyMsg) { + return legacyMsg->header; + } else { + return {}; + } + }; + + return std::visit(visit_functor, message); +} + +std::vector
accountKeys(const VersionedMessage& message) { + auto visit_functor = [](const VersionedMessage& message) -> std::vector
{ + if (auto* msg = std::get_if(&message); msg) { + return msg->msg.accountKeys; + } else if (auto* legacyMsg = std::get_if(&message); legacyMsg) { + return legacyMsg->accountKeys; + } else { + return {}; + } + }; + + return std::visit(visit_functor, message); +} + +void updateRecentHash(VersionedMessage& message, const Data& recentHash) { + if (auto* msg = std::get_if(&message); msg) { + msg->msg.mRecentBlockHash = recentHash; + } else if (auto* legacyMsg = std::get_if(&message); legacyMsg) { + legacyMsg->mRecentBlockHash = recentHash; + } +} + +} // namespace TW::Solana diff --git a/src/Solana/VersionedMessage.h b/src/Solana/VersionedMessage.h new file mode 100644 index 00000000000..0428945b812 --- /dev/null +++ b/src/Solana/VersionedMessage.h @@ -0,0 +1,21 @@ +// Copyright © 2017-2023 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 +#include "Solana/V0Message.h" +#include "Solana/LegacyMessage.h" +#include "Solana/MessageHeader.h" + +namespace TW::Solana { + using VersionedMessage = std::variant; + + Data serialize(const VersionedMessage& message); + MessageHeader header(const VersionedMessage& message); + std::vector
accountKeys(const VersionedMessage& message); + void updateRecentHash(VersionedMessage& message, const Data& recentHash); +} diff --git a/src/Solana/VersionedTransaction.cpp b/src/Solana/VersionedTransaction.cpp new file mode 100644 index 00000000000..548617f09be --- /dev/null +++ b/src/Solana/VersionedTransaction.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2023 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 "Solana/VersionedTransaction.h" +#include "Solana/Encoding.h" + +namespace TW::Solana { + +std::string VersionedTransaction::serialize() const { + Data buffer; + + append(buffer, shortVecLength(this->signatures)); + for (auto signature : this->signatures) { + Data signature_vec(signature.bytes.begin(), signature.bytes.end()); + append(buffer, signature_vec); + } + append(buffer, this->messageData()); + + return Base58::bitcoin.encode(buffer); +} + +Data VersionedTransaction::messageData() const { + return Solana::serialize(this->message); +} + +uint8_t VersionedTransaction::getAccountIndex(Address publicKey) { + const auto accountKeys = Solana::accountKeys(this->message); + auto item = std::find(accountKeys.begin(), accountKeys.end(), publicKey); + if (item == accountKeys.end()) { + throw std::invalid_argument("publicKey not found in message.accountKeys"); + } + return (uint8_t)std::distance(accountKeys.begin(), item); +} +} // namespace TW::Solana diff --git a/src/Solana/VersionedTransaction.h b/src/Solana/VersionedTransaction.h new file mode 100644 index 00000000000..02af2d377be --- /dev/null +++ b/src/Solana/VersionedTransaction.h @@ -0,0 +1,46 @@ +// Copyright © 2017-2023 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 "Solana/Address.h" +#include "Solana/VersionedMessage.h" +#include "Solana/Signature.h" +#include "Data.h" +#include "BinaryCoding.h" + +#include +#include + +namespace TW::Solana { + +class VersionedTransaction { + public: + // Signatures + std::vector signatures; + // The message to sign + VersionedMessage message; + + VersionedTransaction(const VersionedMessage& message) : message(message) { + this->signatures.resize(header(message).numRequiredSignatures, Signature(defaultSignature)); + } + + // Default basic transfer transaction + VersionedTransaction(const Address& from, const Address& to, uint64_t value, Data recentBlockhash, std::string memo = "", std::vector
references = {}) + : message(VersionedMessage(LegacyMessage::createTransfer(from, to, value, recentBlockhash, memo, references))) { + this->signatures.resize(1, Signature(defaultSignature)); + } + + public: + std::string serialize() const; + std::vector messageData() const; + uint8_t getAccountIndex(Address publicKey); + + private: + TW::Data defaultSignature = TW::Data(64); +}; + +} // namespace TW::Solana diff --git a/src/proto/Solana.proto b/src/proto/Solana.proto index 07fb6a00f58..67a250cbffe 100644 --- a/src/proto/Solana.proto +++ b/src/proto/Solana.proto @@ -138,17 +138,19 @@ message SigningInput { // Relatively recent block hash string recent_blockhash = 2; + bool v0_msg = 3; + // Payload message oneof transaction_type { - Transfer transfer_transaction = 3; - DelegateStake delegate_stake_transaction = 4; - DeactivateStake deactivate_stake_transaction = 5; - DeactivateAllStake deactivate_all_stake_transaction = 6; - WithdrawStake withdraw_transaction = 7; - WithdrawAllStake withdraw_all_transaction = 8; - CreateTokenAccount create_token_account_transaction = 9; - TokenTransfer token_transfer_transaction = 10; - CreateAndTransferToken create_and_transfer_token_transaction = 11; + Transfer transfer_transaction = 4; + DelegateStake delegate_stake_transaction = 5; + DeactivateStake deactivate_stake_transaction = 6; + DeactivateAllStake deactivate_all_stake_transaction = 7; + WithdrawStake withdraw_transaction = 8; + WithdrawAllStake withdraw_all_transaction = 9; + CreateTokenAccount create_token_account_transaction = 10; + TokenTransfer token_transfer_transaction = 11; + CreateAndTransferToken create_and_transfer_token_transaction = 12; } } diff --git a/tests/chains/Solana/ProgramTests.cpp b/tests/chains/Solana/ProgramTests.cpp index f943622e28b..2b443c6ddfb 100644 --- a/tests/chains/Solana/ProgramTests.cpp +++ b/tests/chains/Solana/ProgramTests.cpp @@ -27,14 +27,14 @@ TEST(SolanaStakeProgram, addressFromValidatorSeed) { TEST(SolanaStakeProgram, addressFromRecentBlockhash) { { auto user = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); + Data recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); auto programId = Address("Stake11111111111111111111111111111111111111"); auto expected = Address("GQDDc5EVGJZFC7AvpEJ8eoCQ75Yy4gr7eu17frCjvQRQ"); EXPECT_EQ(StakeProgram::addressFromRecentBlockhash(user, recentBlockhash, programId), expected); } { auto user = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Solana::Hash recentBlockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + Data recentBlockhash = Base58::bitcoin.decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); auto programId = Address("Stake11111111111111111111111111111111111111"); auto expected = Address("2Kos1xJRBq3Ae1GnVNBx7HgJhq8KvdUe2bXE4QGdNaXb"); EXPECT_EQ(StakeProgram::addressFromRecentBlockhash(user, recentBlockhash, programId), expected); diff --git a/tests/chains/Solana/SignerTests.cpp b/tests/chains/Solana/SignerTests.cpp index 875300fb5e2..b4ee4910b8e 100644 --- a/tests/chains/Solana/SignerTests.cpp +++ b/tests/chains/Solana/SignerTests.cpp @@ -92,8 +92,8 @@ TEST(SolanaSigner, SingleSignTransaction) { const auto from = Address(publicKey); auto to = Address("EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); std::vector signerKeys; signerKeys.push_back(privateKey); @@ -132,8 +132,8 @@ TEST(SolanaSigner, SignTransactionToSelf) { const auto from = Address(publicKey); auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); std::vector signerKeys; signerKeys.push_back(privateKey); @@ -177,15 +177,15 @@ TEST(SolanaSigner, MultipleSignTransaction) { MessageHeader header = {2, 0, 1}; std::vector
accountKeys = {address0, address1, programId}; - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - Message message; + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + LegacyMessage message; message.header = header; message.accountKeys = accountKeys; - message.recentBlockhash = recentBlockhash; + message.mRecentBlockHash = recentBlockhash; message.instructions = instructions; message.compileInstructions(); - auto transaction = Transaction(message); + auto transaction = VersionedTransaction(VersionedMessage(message)); std::vector signerKeys; // Sign order should not matter @@ -220,14 +220,14 @@ TEST(SolanaSigner, SignUpdateBlockhash) { const auto from = Address(publicKey); auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); std::vector signerKeys; signerKeys.push_back(privateKey); Signer::sign(signerKeys, transaction); - Solana::Hash newBlockhash("GgBaCs3NCBuZN12kCJgAW63ydqohFkHEdfdEXBPzLHq"); + auto newBlockhash = Base58::bitcoin.decode("GgBaCs3NCBuZN12kCJgAW63ydqohFkHEdfdEXBPzLHq"); Signer::signUpdateBlockhash(signerKeys, transaction, newBlockhash); std::vector expectedSignatures; @@ -279,11 +279,11 @@ TEST(SolanaSigner, SignDelegateStakeV2) { auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); auto programId = Address("Stake11111111111111111111111111111111111111"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); - auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = Transaction(message); + auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); + auto transaction = VersionedTransaction(VersionedMessage(message)); std::vector signerKeys; signerKeys.push_back(privateKeySigner); @@ -307,11 +307,11 @@ TEST(SolanaSigner, SignDelegateStakeV1) { auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); auto programId = Address("Stake11111111111111111111111111111111111111"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); - auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = Transaction(message); + auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); + auto transaction = VersionedTransaction(VersionedMessage(message)); std::vector signerKeys; signerKeys.push_back(privateKeySigner); @@ -335,10 +335,10 @@ TEST(SolanaSigner, SignCreateTokenAccount) { auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - Solana::Hash recentBlockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + auto recentBlockhash = Base58::bitcoin.decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - auto message = Message::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); - auto transaction = Transaction(message); + auto message = LegacyMessage::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); + auto transaction = VersionedTransaction(VersionedMessage(message)); std::vector signerKeys; signerKeys.push_back(privateKeySigner); @@ -365,10 +365,10 @@ TEST(SolanaSigner, SignCreateTokenAccountForOther_3E6UFV) { auto otherMainAddress = Address("3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft"); auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); auto tokenAddress = Address("67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3"); - Solana::Hash recentBlockhash("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); + auto recentBlockhash = Base58::bitcoin.decode("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); - auto message = Message::createTokenCreateAccount(signer, otherMainAddress, token, tokenAddress, recentBlockhash); - auto transaction = Transaction(message); + auto message = LegacyMessage::createTokenCreateAccount(signer, otherMainAddress, token, tokenAddress, recentBlockhash); + auto transaction = VersionedTransaction(VersionedMessage(message)); std::vector signerKeys; signerKeys.push_back(privateKeySigner); @@ -392,11 +392,11 @@ TEST(SolanaSigner, SignTransferToken_3vZ67C) { auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); uint64_t amount = 4000; uint8_t decimals = 6; - Solana::Hash recentBlockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); + auto recentBlockhash = Base58::bitcoin.decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); - auto message = Message::createTokenTransfer(signer, token, + auto message = LegacyMessage::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); - auto transaction = Transaction(message); + auto transaction = VersionedTransaction(VersionedMessage(message)); std::vector signerKeys; signerKeys.push_back(privateKeySigner); diff --git a/tests/chains/Solana/TWAnySignerTests.cpp b/tests/chains/Solana/TWAnySignerTests.cpp index 5fd474bbf0e..de5ee2cd869 100644 --- a/tests/chains/Solana/TWAnySignerTests.cpp +++ b/tests/chains/Solana/TWAnySignerTests.cpp @@ -41,6 +41,24 @@ TEST(TWAnySignerSolana, SignTransfer) { ASSERT_EQ(output.unsigned_tx(), "87PYsiS4MUU1UqXrsDoCBmD5FcKsXhwEBD8hc4zbq78yePu7bLENmbnmjmVbsj4VvaxnZhy4bERndPFzjSRH5WpwKwMLSCKvn9eSDmPESNcdkqne2UdMfWiFoq8ZeQBnF9h98dP8GM9kfzWPjvLmhjwuwA1E2k5WCtfii7LKQ34v6AtmFQGZqgdKiNqygP7ZKusHWGT8ZkTZ"); } +TEST(TWAnySignerSolana, SignV0Transfer) { + // Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet + auto privateKey = parse_hex("833a053c59e78138a3ed090459bc6743cca6a9cbc2809a7bf5dbc7939b8775c8"); + auto input = Proto::SigningInput(); + + auto& message = *input.mutable_transfer_transaction(); + message.set_recipient("6pEfiZjMycJY4VA2FtAbKgYvRwzXDpxY58Xp4b7FQCz9"); + message.set_value((uint64_t)5000L); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("HxKwWFTHixCu8aw35J1uxAX6yUhLHkFCdJJdK4y98Gyj"); + input.set_v0_msg(true); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + ASSERT_EQ(output.encoded(), "6NijVxwQoDjqt6A41HXCK9kXwNDp48uLgvRyE8uz6NY5dEzaEDLzjzuMnc5TGatHZZUXehKrzUGzbg9jPSdn6pVsMc9TXNH6JGe5RJLmHwWey3MC1p8Hs2zhjw5P439P57NToatraDX9ZwvBtK4EzZzRjWbyGdicheTPjeYKCzvPCLxDkTFtPCM9VZGGXSN2Bne92NLDvf6ntNm5pxsPkZGxPe4w9Eq26gkE83hZyrYXKaiDh8TbqbHatSkw"); +} + TEST(TWAnySignerSolana, SignTransferToSelf) { auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Proto::SigningInput(); diff --git a/tests/chains/Solana/TransactionTests.cpp b/tests/chains/Solana/TransactionTests.cpp index c6ff726f613..c8c4458ec3e 100644 --- a/tests/chains/Solana/TransactionTests.cpp +++ b/tests/chains/Solana/TransactionTests.cpp @@ -18,7 +18,7 @@ namespace TW::Solana { TEST(SolanaTransaction, TransferMessageData) { auto from = Address("6eoo7i1khGhVm8tLBMAdq4ax2FxkKP4G7mCcfHyr3STN"); auto to = Address("56B334QvCDMSirsmtEJGfanZm8GqeQarrSjdAb2MbeNM"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); auto transaction = Transaction(from, to, 42, recentBlockhash); auto expectedHex = @@ -32,7 +32,7 @@ TEST(SolanaTransaction, TransferMessageData) { TEST(SolanaTransaction, TransferSerializeTransaction) { auto from = Address("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5"); auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); auto transaction = Transaction(from, to, 42, recentBlockhash); Signature signature( "46SRiQGvtPb1iivDfnuC3dW1GzXkfQPTjdUyvFqF2sdPvFrsfx94fys2xpNKR6UiAj7RgKWdJG6mEfe85up6i1JT"); @@ -50,7 +50,7 @@ TEST(SolanaTransaction, TransferSerializeTransaction) { TEST(SolanaTransaction, TransferTransactionPayToSelf) { auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); auto transaction = Transaction(from, to, 42, recentBlockhash); Signature signature( "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); @@ -67,7 +67,7 @@ TEST(SolanaTransaction, TransferTransactionPayToSelf) { TEST(SolanaTransaction, TransferWithMemoAndReferenceTransaction) { const auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); const auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - const Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); const auto memo = "HelloSolana73"; std::vector
references = {Address("GaeTAQZyhVEocTC7iY8GztSyY5cBAJTkAUUA1kLFLMV")}; auto transaction = Transaction(from, to, 42, recentBlockhash, memo, references); @@ -83,9 +83,9 @@ TEST(SolanaTransaction, StakeSerializeTransactionV2) { auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); auto programId = Address("Stake11111111111111111111111111111111111111"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); - auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); + auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); auto transaction = Transaction(message); Signature signature( "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); @@ -100,9 +100,9 @@ TEST(SolanaTransaction, StakeSerializeTransactionV1) { auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); auto programId = Address("Stake11111111111111111111111111111111111111"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); - auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); + auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); auto transaction = Transaction(message); Signature signature( "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); @@ -117,8 +117,8 @@ TEST(SolanaTransaction, CreateTokenAccountTransaction) { auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - Solana::Hash recentBlockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - auto message = Message::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); + auto recentBlockhash = Base58::bitcoin.decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + auto message = LegacyMessage::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); EXPECT_EQ(message.header.numRequiredSignatures, 1); EXPECT_EQ(message.header.numCreditOnlySignedAccounts, 0); EXPECT_EQ(message.header.numCreditOnlyUnsignedAccounts, 5); @@ -130,7 +130,7 @@ TEST(SolanaTransaction, CreateTokenAccountTransaction) { EXPECT_EQ(message.accountKeys[4].string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); EXPECT_EQ(message.accountKeys[5].string(), "SysvarRent111111111111111111111111111111111"); EXPECT_EQ(message.accountKeys[6].string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - EXPECT_EQ(Base58::bitcoin.encode(message.recentBlockhash.bytes), "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + EXPECT_EQ(Base58::bitcoin.encode(message.mRecentBlockHash), "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); ASSERT_EQ(message.instructions.size(), 1ul); EXPECT_EQ(message.instructions[0].programId.string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); ASSERT_EQ(message.instructions[0].accounts.size(), 7ul); @@ -159,8 +159,8 @@ TEST(SolanaTransaction, TransferTokenTransaction_3vZ67C) { auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); uint64_t amount = 4000; uint8_t decimals = 6; - Solana::Hash recentBlockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); - auto message = Message::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); + auto recentBlockhash = Base58::bitcoin.decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); + auto message = LegacyMessage::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); EXPECT_EQ(message.header.numRequiredSignatures, 1); EXPECT_EQ(message.header.numCreditOnlySignedAccounts, 0); EXPECT_EQ(message.header.numCreditOnlyUnsignedAccounts, 2); From 1dabdbe26bbf0067e62270aba2bac1ecedd11cdb Mon Sep 17 00:00:00 2001 From: Tyera Date: Tue, 21 Feb 2023 16:55:56 -0700 Subject: [PATCH 106/426] [Solana] Refine `programId` and `accounts` comments, `CreditOnly` -> `ReadOnly` (#2939) * Fixup Instruction comments * Update MessageHeader field names --- src/Solana/Instruction.h | 6 ++---- src/Solana/LegacyMessage.cpp | 4 ++-- src/Solana/MessageHeader.h | 12 ++++++------ tests/chains/Solana/TransactionTests.cpp | 8 ++++---- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Solana/Instruction.h b/src/Solana/Instruction.h index 7c9dd24cc05..535fcb25ccd 100644 --- a/src/Solana/Instruction.h +++ b/src/Solana/Instruction.h @@ -38,11 +38,9 @@ enum TokenInstruction { // An instruction to execute a program struct Instruction { - // Index into the transaction keys array indicating the program account that - // executes this instruction + // The address of the program account that executes this instruction Address programId; - // Ordered indices into the transaction keys array indicating which accounts - // to pass to the program + // List of accounts to pass to the program std::vector accounts; // The program input data Data data; diff --git a/src/Solana/LegacyMessage.cpp b/src/Solana/LegacyMessage.cpp index 42f411578c9..d997465e8b9 100644 --- a/src/Solana/LegacyMessage.cpp +++ b/src/Solana/LegacyMessage.cpp @@ -37,8 +37,8 @@ Data LegacyMessage::serialize() const { Data buffer; buffer.push_back(this->header.numRequiredSignatures); - buffer.push_back(this->header.numCreditOnlySignedAccounts); - buffer.push_back(this->header.numCreditOnlyUnsignedAccounts); + buffer.push_back(this->header.numReadOnlySignedAccounts); + buffer.push_back(this->header.numReadOnlyUnsignedAccounts); append(buffer, shortVecLength
(this->accountKeys)); for (auto account_key : this->accountKeys) { Data account_key_vec(account_key.bytes.begin(), account_key.bytes.end()); diff --git a/src/Solana/MessageHeader.h b/src/Solana/MessageHeader.h index bb06ce56076..f01c2142305 100644 --- a/src/Solana/MessageHeader.h +++ b/src/Solana/MessageHeader.h @@ -13,10 +13,10 @@ struct MessageHeader { // valid. The signatures must match the first `numRequiredSignatures` of // `accountKeys`. uint8_t numRequiredSignatures = 0; - // The last numCreditOnlySignedAccounts of the signed keys are - // credit-only accounts. - uint8_t numCreditOnlySignedAccounts = 0; - // The last numCreditOnlyUnsignedAccounts of the unsigned keys are - // credit-only accounts. - uint8_t numCreditOnlyUnsignedAccounts = 0; + // The last numRequiredSignatures of the signed keys are + // read-only accounts. + uint8_t numReadOnlySignedAccounts = 0; + // The last numReadOnlyUnsignedAccounts of the unsigned keys are + // read-only accounts. + uint8_t numReadOnlyUnsignedAccounts = 0; }; diff --git a/tests/chains/Solana/TransactionTests.cpp b/tests/chains/Solana/TransactionTests.cpp index c8c4458ec3e..0558f31605e 100644 --- a/tests/chains/Solana/TransactionTests.cpp +++ b/tests/chains/Solana/TransactionTests.cpp @@ -120,8 +120,8 @@ TEST(SolanaTransaction, CreateTokenAccountTransaction) { auto recentBlockhash = Base58::bitcoin.decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); auto message = LegacyMessage::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numCreditOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numCreditOnlyUnsignedAccounts, 5); + EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); + EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 5); ASSERT_EQ(message.accountKeys.size(), 7ul); EXPECT_EQ(message.accountKeys[0].string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); EXPECT_EQ(message.accountKeys[1].string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); @@ -162,8 +162,8 @@ TEST(SolanaTransaction, TransferTokenTransaction_3vZ67C) { auto recentBlockhash = Base58::bitcoin.decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); auto message = LegacyMessage::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numCreditOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numCreditOnlyUnsignedAccounts, 2); + EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); + EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 2); ASSERT_EQ(message.accountKeys.size(), 5ul); ASSERT_EQ(message.instructions.size(), 1ul); EXPECT_EQ(message.instructions[0].programId.string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); From 90b18837742fca65edfaaf0ca2fececf51bfbc1e Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 22 Feb 2023 14:16:48 +0100 Subject: [PATCH 107/426] [Base58]: Move base58 implementation to rust (#2944) --- rust/Cargo.lock | 7 + rust/Cargo.toml | 1 + rust/src/encoding/base32.rs | 12 +- rust/src/encoding/base58.rs | 99 +++++++++ rust/src/encoding/mod.rs | 1 + rust/src/memory/mod.rs | 10 + src/Aeternity/Address.cpp | 6 +- src/Aeternity/Signer.cpp | 2 +- src/Aeternity/Transaction.cpp | 2 +- src/Base58.cpp | 196 ------------------ src/Base58.h | 85 ++++---- src/Base58Address.h | 8 +- src/Bitcoin/Script.cpp | 2 +- src/Cardano/AddressV2.cpp | 4 +- src/Decred/Address.cpp | 6 +- src/EOS/Address.cpp | 4 +- src/EOS/Transaction.cpp | 2 +- src/FIO/Address.cpp | 4 +- src/FIO/Signer.cpp | 2 +- src/Groestlcoin/Address.cpp | 8 +- src/HDWallet.cpp | 4 +- src/NEAR/Address.cpp | 2 +- src/NEO/Address.cpp | 2 +- src/NULS/Address.cpp | 6 +- src/Nebulas/Address.cpp | 6 +- src/Polkadot/SS58Address.cpp | 6 +- src/Solana/Address.cpp | 6 +- src/Solana/LegacyMessage.h | 2 +- src/Solana/Program.cpp | 2 +- src/Solana/Signature.h | 2 +- src/Solana/Signer.cpp | 4 +- src/Solana/Transaction.cpp | 2 +- src/Solana/VersionedTransaction.cpp | 2 +- src/Tezos/Address.cpp | 6 +- src/Tezos/BinaryCoding.cpp | 6 +- src/Tezos/Forging.cpp | 4 +- src/Tezos/OperationList.cpp | 4 +- src/Tron/Address.cpp | 2 +- src/Tron/Signer.cpp | 34 +-- src/Waves/Address.cpp | 6 +- src/Waves/Transaction.cpp | 24 +-- src/XRP/Address.cpp | 6 +- src/XRP/XAddress.cpp | 6 +- src/interface/TWBase58.cpp | 8 +- .../chains/Bitcoin/TWBitcoinSigningTests.cpp | 2 +- tests/chains/FIO/SignerTests.cpp | 2 +- tests/chains/NEAR/AddressTests.cpp | 2 +- tests/chains/NEAR/SerializationTests.cpp | 54 ++--- tests/chains/NEAR/SignerTests.cpp | 6 +- tests/chains/NEAR/TWAnySignerTests.cpp | 10 +- tests/chains/Nebulas/AddressTests.cpp | 4 +- tests/chains/Solana/AddressTests.cpp | 16 +- tests/chains/Solana/ProgramTests.cpp | 18 +- tests/chains/Solana/SignerTests.cpp | 62 +++--- tests/chains/Solana/TWAnySignerTests.cpp | 34 +-- tests/chains/Solana/TransactionTests.cpp | 18 +- tests/chains/Tezos/SignerTests.cpp | 2 +- tests/chains/Waves/TWAnySignerTests.cpp | 2 +- 58 files changed, 376 insertions(+), 469 deletions(-) create mode 100644 rust/src/encoding/base58.rs delete mode 100644 src/Base58.cpp diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 601d9de59d3..ca96211a88d 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -143,6 +143,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + [[package]] name = "bumpalo" version = "3.12.0" @@ -1040,6 +1046,7 @@ version = "0.1.0" dependencies = [ "base64 0.21.0", "bcs", + "bs58", "hex", "move-core-types", "starknet-crypto", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 7752a93202d..939ac5c6dbb 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -17,5 +17,6 @@ starknet-signers = "0.1.0" bcs = "0.1.4" hex = "0.4.3" base64 = "0.21.0" +bs58 = "0.4.0" [dev-dependencies] diff --git a/rust/src/encoding/base32.rs b/rust/src/encoding/base32.rs index 2d56b74c767..dc9fe7e4ccd 100644 --- a/rust/src/encoding/base32.rs +++ b/rust/src/encoding/base32.rs @@ -94,7 +94,7 @@ fn base32_decode(input: &str, alphabet: Option<&[u8]>, padding: bool) -> Result< } if output == vec![0] { - return Ok(vec![]) + return Ok(vec![]); } Ok(output) } @@ -106,15 +106,11 @@ pub extern "C" fn decode_base32(input: *const c_char, alphabet: *const c_char, p match base32_decode(input, alphabet, padding) { Ok(decoded) => { - let size = decoded.len(); - let mut decoded_vec = decoded.to_vec(); - let ptr = decoded_vec.as_mut_ptr(); - std::mem::forget(decoded_vec); - CByteArray { data: ptr, size } - }, + decoded.into() + } Err(_) => { CByteArray { data: std::ptr::null_mut(), size: 0 } - }, + } } } diff --git a/rust/src/encoding/base58.rs b/rust/src/encoding/base58.rs new file mode 100644 index 00000000000..d9c3da459bb --- /dev/null +++ b/rust/src/encoding/base58.rs @@ -0,0 +1,99 @@ +// Copyright © 2017-2023 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. + +use std::ffi::{c_char, CStr, CString}; +use bs58::{encode, decode, Alphabet}; +use crate::memory::CByteArray; + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub enum Base58Alphabet { + Bitcoin = 1, + Ripple = 2, +} + +impl From for &Alphabet { + fn from(value: Base58Alphabet) -> Self { + match value { + Base58Alphabet::Bitcoin => Alphabet::BITCOIN, + Base58Alphabet::Ripple => Alphabet::RIPPLE + } + } +} + +fn base58_encode(input: &[u8], alphabet: Base58Alphabet) -> String { + encode(input) + .with_alphabet(alphabet.into()) + .into_string() +} + +#[no_mangle] +pub extern "C" fn encode_base58(input: *const u8, input_len: usize, alphabet: Base58Alphabet) -> *mut c_char { + let input = unsafe { std::slice::from_raw_parts(input, input_len) }; + CString::new(base58_encode(input, alphabet)).unwrap().into_raw() +} + +fn base58_decode(input: &str, alphabet: Base58Alphabet) -> decode::Result> { + decode(input).with_alphabet(alphabet.into()).into_vec() +} + +#[no_mangle] +pub extern "C" fn decode_base58(input: *const c_char, alphabet: Base58Alphabet) -> CByteArray { + let input = unsafe { CStr::from_ptr(input).to_str().unwrap() }; + + match base58_decode(input, alphabet) { + Ok(decoded) => { + decoded.into() + }, + Err(_) => { + CByteArray { data: std::ptr::null_mut(), size: 0 } + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_base58_encode() { + let data = b"Hello, world!"; + let expected = "72k1xXWG59wUsYv7h2"; + + let result = base58_encode(data, Base58Alphabet::Bitcoin); + assert_eq!(result, expected); + } + + #[test] + fn test_base58_decode() { + let data = "72k1xXWG59wUsYv7h2"; + let expected = b"Hello, world!"; + + let result = base58_decode(data, Base58Alphabet::Bitcoin).unwrap(); + assert_eq!(result, expected.to_vec()); + } + + #[test] + fn test_base58_encode_ffi() { + let data = b"Hello, world!"; + let expected = "72k1xXWG59wUsYv7h2"; + + let result_ptr = encode_base58(data.as_ptr(), data.len(), Base58Alphabet::Bitcoin); + let result = unsafe { CString::from_raw(result_ptr) }; + assert_eq!(result.to_str().unwrap(), expected); + } + + #[test] + fn test_base58_decode_ffi() { + let data = "72k1xXWG59wUsYv7h2"; + let expected = b"Hello, world!"; + + let input = CString::new(data).unwrap(); + let decoded_ptr = decode_base58(input.as_ptr(), Base58Alphabet::Bitcoin); + let decoded_slice = unsafe { std::slice::from_raw_parts(decoded_ptr.data, decoded_ptr.size) }; + assert_eq!(decoded_slice, expected); + } +} diff --git a/rust/src/encoding/mod.rs b/rust/src/encoding/mod.rs index f0648e99e5a..9face5a38ec 100644 --- a/rust/src/encoding/mod.rs +++ b/rust/src/encoding/mod.rs @@ -5,5 +5,6 @@ // file LICENSE at the root of the source code distribution tree. mod base32; +mod base58; mod base64; mod hex; diff --git a/rust/src/memory/mod.rs b/rust/src/memory/mod.rs index a923e03a8c0..4376cf6ea14 100644 --- a/rust/src/memory/mod.rs +++ b/rust/src/memory/mod.rs @@ -12,6 +12,16 @@ pub struct CByteArray { pub size: usize, } +impl From> for CByteArray { + fn from(value: Vec) -> Self { + let size = value.len(); + let mut mut_vec = value.to_vec(); + let ptr = mut_vec.as_mut_ptr(); + std::mem::forget(mut_vec); + CByteArray { data: ptr, size } + } +} + #[no_mangle] pub unsafe extern fn free_string(ptr: *const c_char) { // Take the ownership back to rust and drop the owner diff --git a/src/Aeternity/Address.cpp b/src/Aeternity/Address.cpp index b3d0ecc64fb..b95f5bcede3 100644 --- a/src/Aeternity/Address.cpp +++ b/src/Aeternity/Address.cpp @@ -38,12 +38,12 @@ Address::Address(const std::string& string) { } auto payload = string.substr(Identifiers::prefixAccountPubkey.size(), string.size()); - bytes = Base58::bitcoin.decodeCheck(payload); + bytes = Base58::decodeCheck(payload); } /// Returns a string representation of the Aeternity address. std::string Address::string() const { - return Identifiers::prefixAccountPubkey + Base58::bitcoin.encodeCheck(bytes); + return Identifiers::prefixAccountPubkey + Base58::encodeCheck(bytes); } bool Address::checkType(const std::string& type) { @@ -51,7 +51,7 @@ bool Address::checkType(const std::string& type) { } bool Address::checkPayload(const std::string& payload) { - unsigned long base58 = Base58::bitcoin.decodeCheck(payload).size(); + unsigned long base58 = Base58::decodeCheck(payload).size(); return base58 == size; } diff --git a/src/Aeternity/Signer.cpp b/src/Aeternity/Signer.cpp index ff27db8d81a..a2f314bba4d 100644 --- a/src/Aeternity/Signer.cpp +++ b/src/Aeternity/Signer.cpp @@ -37,7 +37,7 @@ Proto::SigningOutput Signer::sign(const TW::PrivateKey& privateKey, Transaction& /// sign ed25519 auto sigRaw = privateKey.sign(msg, TWCurveED25519); - auto signature = Identifiers::prefixSignature + Base58::bitcoin.encodeCheck(sigRaw); + auto signature = Identifiers::prefixSignature + Base58::encodeCheck(sigRaw); /// encode the message using rlp auto rlpTxRaw = buildRlpTxRaw(txRlp, sigRaw); diff --git a/src/Aeternity/Transaction.cpp b/src/Aeternity/Transaction.cpp index 7ed0515b97b..32690b4cebf 100644 --- a/src/Aeternity/Transaction.cpp +++ b/src/Aeternity/Transaction.cpp @@ -34,7 +34,7 @@ TW::Data Transaction::buildTag(const std::string& address) { auto data = Data(); append(data, Identifiers::iDTagAccount); - append(data, Base58::bitcoin.decodeCheck(payload)); + append(data, Base58::decodeCheck(payload)); return data; } diff --git a/src/Base58.cpp b/src/Base58.cpp deleted file mode 100644 index 61ed8d4600b..00000000000 --- a/src/Base58.cpp +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright © 2014-2018 The Bitcoin Core developers -// 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 "Base58.h" - -#include "Hash.h" - -#include -#include -#include -#include - -using namespace TW; - -// clang-format off - -static const std::array bitcoinDigits = { - '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' -}; - -static const std::array bitcoinCharacterMap = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1, - -1, 9,10,11,12,13,14,15,16,-1,17,18,19,20,21,-1, - 22,23,24,25,26,27,28,29,30,31,32,-1,-1,-1,-1,-1, - -1,33,34,35,36,37,38,39,40,41,42,43,-1,44,45,46, - 47,48,49,50,51,52,53,54,55,56,57,-1,-1,-1,-1,-1, -}; - -static const std::array rippleDigits = { - 'r', 'p', 's', 'h', 'n', 'a', 'f', '3', '9', 'w', 'B', 'U', 'D', 'N', 'E', - 'G', 'H', 'J', 'K', 'L', 'M', '4', 'P', 'Q', 'R', 'S', 'T', '7', 'V', 'W', - 'X', 'Y', 'Z', '2', 'b', 'c', 'd', 'e', 'C', 'g', '6', '5', 'j', 'k', 'm', - '8', 'o', 'F', 'q', 'i', '1', 't', 'u', 'v', 'A', 'x', 'y', 'z' -}; - -static const std::array rippleCharacterMap = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,50,33,7,21,41,40,27,45,8,-1,-1,-1,-1,-1,-1, - -1,54,10,38,12,14,47,15,16,-1,17,18,19,20,13,-1, - 22,23,24,25,26,11,28,29,30,31,32,-1,-1,-1,-1,-1, - -1,5,34,35,36,37,6,39,3,49,42,43,-1,44,4,46, - 1,48,0,2,51,52,53,9,55,56,57,-1,-1,-1,-1,-1, -}; - -// clang-format on - -Base58 Base58::bitcoin = Base58(bitcoinDigits, bitcoinCharacterMap); - -Base58 Base58::ripple = Base58(rippleDigits, rippleCharacterMap); - -Data Base58::decodeCheck(const char* begin, const char* end, Hash::Hasher hasher) const { - auto result = decode(begin, end); - if (result.size() < 4) { - return {}; - } - - // re-calculate the checksum, ensure it matches the included 4-byte checksum - auto hash = Hash::hash(hasher, result.data(), result.size() - 4); - if (!std::equal(hash.begin(), hash.begin() + 4, result.end() - 4)) { - return {}; - } - - return Data(result.begin(), result.end() - 4); -} - -Data Base58::decode(const char* begin, const char* end) const { - const auto* it = begin; - - // Skip leading spaces. - it = std::find_if_not(it, end, [](char c) { return std::isspace(c);}); - - // Skip and count leading zeros. - std::size_t zeroes = 0; - std::size_t length = 0; - while (it != end && *it == digits[0]) { - zeroes += 1; - it += 1; - } - - // Allocate enough space in big-endian base256 representation. - std::size_t base258Size = (end - it) * 733 / 1000 + 1; // log(58) / log(256), rounded up. - Data b256(base258Size); - - // Process the characters. - while (it != end && !std::isspace(*it)) { - if (static_cast(*it) >= 128) { - // Invalid b58 character - return {}; - } - - // Decode base58 character - int carry = characterMap[static_cast(*it)]; - if (carry == -1) { - // Invalid b58 character - return {}; - } - - std::size_t i = 0; - for (auto b256it = b256.rbegin(); (carry != 0 || i < length) && (b256it != b256.rend()); - ++b256it, ++i) { - carry += 58 * (*b256it); - *b256it = static_cast(carry % 256); - carry /= 256; - } - assert(carry == 0); - length = i; - it += 1; - } - - // Skip trailing spaces. - it = std::find_if_not(it, end, [](char c) { return std::isspace(c);}); - if (it != end) { - // Extra charaters at the end - return {}; - } - - // Skip leading zeroes in b256. - auto b256it = b256.begin() + (base258Size - length); - while (b256it != b256.end() && *b256it == 0) { - b256it++; - } - - // Copy result into output vector. - Data result; - result.reserve(zeroes + (b256.end() - b256it)); - result.assign(zeroes, 0x00); - std::copy(b256it, b256.end(), std::back_inserter(result)); - - return result; -} - -std::string Base58::encodeCheck(const byte* begin, const byte* end, Hash::Hasher hasher) const { - // add 4-byte hash check to the end - Data dataWithCheck(begin, end); - auto hash = Hash::hash(hasher, begin, end - begin); - dataWithCheck.insert(dataWithCheck.end(), hash.begin(), hash.begin() + 4); - return encode(dataWithCheck); -} - -std::string Base58::encode(const byte* begin, const byte* end) const { - // Skip & count leading zeroes. - int zeroes = 0; - int length = 0; - while (begin != end && *begin == 0) { - begin += 1; - zeroes += 1; - } - - // Allocate enough space in big-endian base58 representation. - auto base58Size = (end - begin) * 138 / 100 + 1; // log(256) / log(58), rounded up. - Data b58(base58Size); - - while (begin != end) { - int carry = *begin; - int i = 0; - // Apply "b58 = b58 * 256 + ch". - for (auto b58it = b58.rbegin(); (carry != 0 || i < length) && (b58it != b58.rend()); - b58it++, i++) { - carry += 256 * (*b58it); - *b58it = carry % 58; - carry /= 58; - } - - assert(carry == 0); - length = i; - begin += 1; - } - - // Skip leading zeroes in base58 result. - auto it = b58.begin() + (base58Size - length); - while (it != b58.end() && *it == 0) { - it++; - } - - // Translate the result into a string. - std::string str; - str.reserve(zeroes + (b58.end() - it)); - str.assign(zeroes, digits[0]); - while (it != b58.end()) { - str += digits[*it]; - it += 1; - } - return str; -} diff --git a/src/Base58.h b/src/Base58.h index d5e231d307d..6091b316646 100644 --- a/src/Base58.h +++ b/src/Base58.h @@ -8,64 +8,53 @@ #include "Data.h" #include "Hash.h" +#include "rust/bindgen/WalletCoreRSBindgen.h" -#include #include -namespace TW { - -class Base58 { - public: - /// Base58 coder with Bitcoin character map. - static Base58 bitcoin; - - /// Base58 coder with Ripple character map. - static Base58 ripple; - - public: - /// Ordered list of valid characters. - const std::array digits; - - /// Maps characters to base58 values. - const std::array characterMap; - - /// Initializes a Base58 class with custom digit mapping. - Base58(const std::array& digits, const std::array& characterMap) - : digits(digits), characterMap(characterMap) {} - - /// Decodes a base 58 string verifying the checksum, returns empty on failure. - Data decodeCheck(const std::string& string, Hash::Hasher hasher = Hash::HasherSha256d) const { - return decodeCheck(string.data(), string.data() + string.size(), hasher); +namespace TW::Base58 { + /// Decodes a base 58 string into `result`, returns `false` on failure. + static inline Data decode(const std::string& string, Base58Alphabet alphabet = Base58Alphabet::Bitcoin) { + if (string.empty()) { + return {}; + } + auto decoded = decode_base58(string.c_str(), alphabet); + if (decoded.data == nullptr || decoded.size == 0) { + return {}; + } + Data decoded_vec(&decoded.data[0], &decoded.data[decoded.size]); + std::free(decoded.data); + return decoded_vec; } - /// Decodes a base 58 string verifying the checksum, returns empty on failure. - Data decodeCheck(const char* begin, const char* end, Hash::Hasher hasher = Hash::HasherSha256d) const; + static inline Data decodeCheck(const std::string& string, Base58Alphabet alphabet = Base58Alphabet::Bitcoin, Hash::Hasher hasher = Hash::HasherSha256d) { + auto result = decode(string, alphabet); + if (result.size() < 4) { + return {}; + } - /// Decodes a base 58 string into `result`, returns `false` on failure. - Data decode(const std::string& string) const { - return decode(string.data(), string.data() + string.size()); - } + // re-calculate the checksum, ensure it matches the included 4-byte checksum + auto hash = Hash::hash(hasher, result.data(), result.size() - 4); + if (!std::equal(hash.begin(), hash.begin() + 4, result.end() - 4)) { + return {}; + } - /// Decodes a base 58 string into `result`, returns `false` on failure. - Data decode(const char* begin, const char* end) const; + return Data(result.begin(), result.end() - 4); + } - /// Encodes data as a base 58 string with a checksum. template - std::string encodeCheck(const T& data, Hash::Hasher hasher = Hash::HasherSha256d) const { - return encodeCheck(data.data(), data.data() + data.size(), hasher); + static inline std::string encode(const T& data, Base58Alphabet alphabet = Base58Alphabet::Bitcoin) { + auto encoded = encode_base58(data.data(), data.size(), alphabet); + std::string encoded_str(encoded); + free_string(encoded); + return encoded_str; } - /// Encodes data as a base 58 string with a checksum. - std::string encodeCheck(const byte* pbegin, const byte* pend, Hash::Hasher hasher = Hash::HasherSha256d) const; - - /// Encodes data as a base 58 string. template - std::string encode(const T& data) const { - return encode(data.data(), data.data() + data.size()); + static inline std::string encodeCheck(const T& data, Base58Alphabet alphabet = Base58Alphabet::Bitcoin, Hash::Hasher hasher = Hash::HasherSha256d) { + auto hash = Hash::hash(hasher, data); + Data toBeEncoded(std::begin(data), std::end(data)); + toBeEncoded.insert(toBeEncoded.end(), hash.begin(), hash.begin() + 4); + return encode(toBeEncoded, alphabet); } - - /// Encodes data as a base 58 string. - std::string encode(const byte* pbegin, const byte* pend) const; -}; - -} // namespace TW +} diff --git a/src/Base58Address.h b/src/Base58Address.h index ee66c495a93..6ec9f4e65ac 100644 --- a/src/Base58Address.h +++ b/src/Base58Address.h @@ -33,7 +33,7 @@ class Base58Address { /// Determines whether a string makes a valid address. static bool isValid(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string); if (decoded.size() != Base58Address::size) { return false; } @@ -43,7 +43,7 @@ class Base58Address { /// Determines whether a string makes a valid address, and the prefix is /// within the valid set. static bool isValid(const std::string& string, const std::vector& validPrefixes) { - const auto decoded = Base58::bitcoin.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string); if (decoded.size() != Base58Address::size) { return false; } @@ -59,7 +59,7 @@ class Base58Address { /// Initializes an address with a string representation. explicit Base58Address(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string); if (decoded.size() != Base58Address::size) { throw std::invalid_argument("Invalid address string"); } @@ -86,7 +86,7 @@ class Base58Address { /// Returns a string representation of the address. std::string string() const { - return Base58::bitcoin.encodeCheck(bytes); + return Base58::encodeCheck(bytes); } }; diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index 08bbaa042f3..473493dfcae 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -342,7 +342,7 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c case TWCoinTypeDecred: if (Decred::Address::isValid(string)) { - auto bytes = Base58::bitcoin.decodeCheck(string, Hash::HasherBlake256d); + auto bytes = Base58::decodeCheck(string, Base58Alphabet::Bitcoin, Hash::HasherBlake256d); if (bytes[1] == TW::p2pkhPrefix(TWCoinTypeDecred)) { return buildPayToPublicKeyHash(Data(bytes.begin() + 2, bytes.end())); } diff --git a/src/Cardano/AddressV2.cpp b/src/Cardano/AddressV2.cpp index 6d2140ab82f..673f47d6c92 100644 --- a/src/Cardano/AddressV2.cpp +++ b/src/Cardano/AddressV2.cpp @@ -15,7 +15,7 @@ namespace TW::Cardano { bool AddressV2::parseAndCheck(const std::string& addr, Data& root_out, Data& attrs_out, byte& type_out) { // Decode Bas58, decode payload + crc, decode root, attr - Data base58decoded = Base58::bitcoin.decode(addr); + Data base58decoded = Base58::decode(addr); if (base58decoded.empty()) { throw std::invalid_argument("Invalid address: could not Base58 decode"); } @@ -100,7 +100,7 @@ Data AddressV2::getCborData() const { std::string AddressV2::string() const { // Base58 encode the CBOR data - return Base58::bitcoin.encode(getCborData()); + return Base58::encode(getCborData()); } Data AddressV2::keyHash(const TW::Data& xpub) { diff --git a/src/Decred/Address.cpp b/src/Decred/Address.cpp index cd3685f9463..81c537a24a7 100644 --- a/src/Decred/Address.cpp +++ b/src/Decred/Address.cpp @@ -17,7 +17,7 @@ static const auto keyhashSize = Hash::ripemdSize; static const auto addressDataSize = keyhashSize + 2; bool Address::isValid(const std::string& string) noexcept { - const auto data = Base58::bitcoin.decodeCheck(string, Hash::HasherBlake256d); + const auto data = Base58::decodeCheck(string, Base58Alphabet::Bitcoin, Hash::HasherBlake256d); if (data.size() != addressDataSize) { return false; } @@ -30,7 +30,7 @@ bool Address::isValid(const std::string& string) noexcept { } Address::Address(const std::string& string) { - const auto data = Base58::bitcoin.decodeCheck(string, Hash::HasherBlake256d); + const auto data = Base58::decodeCheck(string, Base58Alphabet::Bitcoin, Hash::HasherBlake256d); if (data.size() != addressDataSize) { throw std::invalid_argument("Invalid address string"); } @@ -49,7 +49,7 @@ Address::Address(const PublicKey& publicKey) { } std::string Address::string() const { - return Base58::bitcoin.encodeCheck(bytes, Hash::HasherBlake256d); + return Base58::encodeCheck(bytes, Base58Alphabet::Bitcoin, Hash::HasherBlake256d); } } // namespace TW::Decred diff --git a/src/EOS/Address.cpp b/src/EOS/Address.cpp index db97a6d7853..3c5146aa663 100644 --- a/src/EOS/Address.cpp +++ b/src/EOS/Address.cpp @@ -89,7 +89,7 @@ bool Address::extractKeyData(const std::string& string, Address* address) { return false; } - const Data& decodedBytes = Base58::bitcoin.decode(string.substr(prefixSize)); + const Data& decodedBytes = Base58::decode(string.substr(prefixSize)); if (decodedBytes.size() != KeyDataSize) { return false; } @@ -139,7 +139,7 @@ Address::Address(const PublicKey& publicKey, Type type) /// Returns a string representation of the EOS address. std::string Address::string() const { - return prefix() + Base58::bitcoin.encode(keyData); + return prefix() + Base58::encode(keyData); } } // namespace TW::EOS diff --git a/src/EOS/Transaction.cpp b/src/EOS/Transaction.cpp index 72c2791b460..3185fc8bb05 100644 --- a/src/EOS/Transaction.cpp +++ b/src/EOS/Transaction.cpp @@ -57,7 +57,7 @@ std::string Signature::string() const noexcept { buffer.push_back(hash[i]); } - return prefix + TW::Base58::bitcoin.encode(buffer); + return prefix + TW::Base58::encode(buffer); } void Extension::serialize(Data& os) const noexcept { diff --git a/src/FIO/Address.cpp b/src/FIO/Address.cpp index 52d1086e48f..0c983d6c636 100644 --- a/src/FIO/Address.cpp +++ b/src/FIO/Address.cpp @@ -57,7 +57,7 @@ std::optional Address::decodeKeyData(const std::string& string) { return {}; } - const Data& decodedBytes = Base58::bitcoin.decode(string.substr(prefixSize)); + const Data& decodedBytes = Base58::decode(string.substr(prefixSize)); if (decodedBytes.size() != size) { return {}; } @@ -94,7 +94,7 @@ Address::Address(const PublicKey& publicKey) { /// Returns a string representation of the FIO address. std::string Address::string() const { - return prefix() + Base58::bitcoin.encode(bytes); + return prefix() + Base58::encode(bytes); } PublicKey Address::publicKey() const { diff --git a/src/FIO/Signer.cpp b/src/FIO/Signer.cpp index e4390dd3b44..b7d83313b12 100644 --- a/src/FIO/Signer.cpp +++ b/src/FIO/Signer.cpp @@ -47,7 +47,7 @@ std::string Signer::signatureToBase58(const Data& sig) { Data hash = Hash::ripemd(sigWithSuffix); Data sigWithChecksum(sig); append(sigWithChecksum, TW::data(hash.data(), 4)); - string s = SignaturePrefix + Base58::bitcoin.encode(sigWithChecksum); + string s = SignaturePrefix + Base58::encode(sigWithChecksum); return s; } diff --git a/src/Groestlcoin/Address.cpp b/src/Groestlcoin/Address.cpp index 290b097ad69..17339507aff 100644 --- a/src/Groestlcoin/Address.cpp +++ b/src/Groestlcoin/Address.cpp @@ -12,7 +12,7 @@ namespace TW::Groestlcoin { bool Address::isValid(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::HasherGroestl512d); + const auto decoded = Base58::decodeCheck(string, Base58Alphabet::Bitcoin, Hash::HasherGroestl512d); if (decoded.size() != Address::size) { return false; } @@ -21,7 +21,7 @@ bool Address::isValid(const std::string& string) { } bool Address::isValid(const std::string& string, const std::vector& validPrefixes) { - const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::HasherGroestl512d); + const auto decoded = Base58::decodeCheck(string, Base58Alphabet::Bitcoin, Hash::HasherGroestl512d); if (decoded.size() != Address::size) { return false; } @@ -32,7 +32,7 @@ bool Address::isValid(const std::string& string, const std::vector& validP } Address::Address(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::HasherGroestl512d); + const auto decoded = Base58::decodeCheck(string, Base58Alphabet::Bitcoin, Hash::HasherGroestl512d); if (decoded.size() != Address::size) { throw std::invalid_argument("Invalid address string"); } @@ -56,7 +56,7 @@ Address::Address(const PublicKey& publicKey, uint8_t prefix) { } std::string Address::string() const { - return Base58::bitcoin.encodeCheck(bytes, Hash::HasherGroestl512d); + return Base58::encodeCheck(bytes, Base58Alphabet::Bitcoin, Hash::HasherGroestl512d); } } // namespace TW::Groestlcoin diff --git a/src/HDWallet.cpp b/src/HDWallet.cpp index 682bfcddc62..afbe4aedeee 100644 --- a/src/HDWallet.cpp +++ b/src/HDWallet.cpp @@ -348,7 +348,7 @@ std::string serialize(const HDNode* node, uint32_t fingerprint, uint32_t version node_data.insert(node_data.end(), node->private_key, node->private_key + 32); } - return Base58::bitcoin.encodeCheck(node_data, hasher); + return Base58::encodeCheck(node_data, Base58Alphabet::Bitcoin, hasher); } bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher, HDNode* node) { @@ -360,7 +360,7 @@ bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher node->curve = get_curve_by_name(curveNameStr); assert(node->curve != nullptr); - const auto node_data = Base58::bitcoin.decodeCheck(extended, hasher); + const auto node_data = Base58::decodeCheck(extended, Base58Alphabet::Bitcoin, hasher); if (node_data.size() != 78) { return false; } diff --git a/src/NEAR/Address.cpp b/src/NEAR/Address.cpp index 1d669e3d449..f9f7a92ecf1 100644 --- a/src/NEAR/Address.cpp +++ b/src/NEAR/Address.cpp @@ -30,7 +30,7 @@ std::optional Address::decodeLegacyAddress(const std::string& string) { return {}; } - const Data& decoded = Base58::bitcoin.decode(string.substr(prefix.size())); + const Data& decoded = Base58::decode(string.substr(prefix.size())); return Data(decoded.begin(), decoded.end() - 4); } diff --git a/src/NEO/Address.cpp b/src/NEO/Address.cpp index 64c4c28a808..bb77c138e8a 100644 --- a/src/NEO/Address.cpp +++ b/src/NEO/Address.cpp @@ -17,7 +17,7 @@ using namespace TW; namespace TW::NEO { bool Address::isValid(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string); return !(decoded.size() != Address::size || decoded[0] != version); } diff --git a/src/NULS/Address.cpp b/src/NULS/Address.cpp index be8a488e320..c70065d78bf 100644 --- a/src/NULS/Address.cpp +++ b/src/NULS/Address.cpp @@ -27,7 +27,7 @@ bool Address::isValid(const std::string& string) { } std::string address = string.substr(prefix.length(), string.length() - prefix.length()); - Data decoded = Base58::bitcoin.decode(address); + Data decoded = Base58::decode(address); if (decoded.size() != size) { return false; } @@ -56,7 +56,7 @@ Address::Address(const std::string& string) { throw std::invalid_argument("Invalid address string"); } std::string address = string.substr(prefix.length(), string.length() - prefix.length()); - const auto decoded = Base58::bitcoin.decode(address); + const auto decoded = Base58::decode(address); std::copy(decoded.begin(), decoded.end(), bytes.begin()); } @@ -69,7 +69,7 @@ uint8_t Address::type() const { } std::string Address::string() const { - return prefix + Base58::bitcoin.encode(bytes.begin(), bytes.end()); + return prefix + Base58::encode(bytes); } uint8_t Address::checksum(std::array& byteArray) const { diff --git a/src/Nebulas/Address.cpp b/src/Nebulas/Address.cpp index 4b113b6cd8c..6b0cfd8e9f6 100644 --- a/src/Nebulas/Address.cpp +++ b/src/Nebulas/Address.cpp @@ -12,7 +12,7 @@ namespace TW::Nebulas { bool Address::isValid(const std::string& string) { - auto data = Base58::bitcoin.decode(string); + auto data = Base58::decode(string); if (data.size() != (size_t)Address::size) { return false; } @@ -35,7 +35,7 @@ Address::Address(const std::string& string) { throw std::invalid_argument("Invalid address string"); } - auto data = Base58::bitcoin.decode(string); + auto data = Base58::decode(string); std::copy(data.begin(), data.end(), bytes.begin()); } @@ -60,7 +60,7 @@ Address::Address(const PublicKey& publicKey) { } std::string Address::string() const { - return Base58::bitcoin.encode(bytes); + return Base58::encode(bytes); } } // namespace TW::Nebulas diff --git a/src/Polkadot/SS58Address.cpp b/src/Polkadot/SS58Address.cpp index 2c99b764ba9..f87b6395045 100644 --- a/src/Polkadot/SS58Address.cpp +++ b/src/Polkadot/SS58Address.cpp @@ -10,7 +10,7 @@ using namespace TW; using namespace std; bool SS58Address::isValid(const std::string& string, uint32_t network) { - const auto decoded = Base58::bitcoin.decode(string); + const auto decoded = Base58::decode(string); byte decodedNetworkSize = 0; uint32_t decodedNetwork = 0; if (!decodeNetwork(decoded, decodedNetworkSize, decodedNetwork)) { @@ -47,7 +47,7 @@ SS58Address::SS58Address(const std::string& string, uint32_t network) { if (!isValid(string, network)) { throw std::invalid_argument("Invalid address string"); } - const auto decoded = Base58::bitcoin.decode(string); + const auto decoded = Base58::decode(string); bytes.resize(decoded.size() - checksumSize); std::copy(decoded.begin(), decoded.end() - checksumSize, bytes.begin()); } @@ -68,7 +68,7 @@ std::string SS58Address::string() const { auto result = Data(bytes.begin(), bytes.end()); auto checksum = computeChecksum(bytes); append(result, checksum); - return Base58::bitcoin.encode(result); + return Base58::encode(result); } /// Returns public key bytes diff --git a/src/Solana/Address.cpp b/src/Solana/Address.cpp index 3e4f77cface..b1df361aaab 100644 --- a/src/Solana/Address.cpp +++ b/src/Solana/Address.cpp @@ -20,12 +20,12 @@ using namespace TW; namespace TW::Solana { bool Address::isValid(const std::string& string) { - const auto data = Base58::bitcoin.decode(string); + const auto data = Base58::decode(string); return Address::isValid(data); } Address::Address(const std::string& string) { - const auto data = Base58::bitcoin.decode(string); + const auto data = Base58::decode(string); if (!isValid(data)) { throw std::invalid_argument("Invalid address string"); } @@ -48,7 +48,7 @@ Address::Address(const Data& publicKeyData) { } std::string Address::string() const { - return Base58::bitcoin.encode(bytes); + return Base58::encode(bytes); } Data Address::vector() const { diff --git a/src/Solana/LegacyMessage.h b/src/Solana/LegacyMessage.h index f6dca77a5ef..6694e23607d 100644 --- a/src/Solana/LegacyMessage.h +++ b/src/Solana/LegacyMessage.h @@ -35,7 +35,7 @@ class LegacyMessage { std::vector compiledInstructions; LegacyMessage() - : mRecentBlockHash(Base58::bitcoin.decode(NULL_ID_ADDRESS)){}; + : mRecentBlockHash(Base58::decode(NULL_ID_ADDRESS)){}; LegacyMessage(Data recentBlockHash, const std::vector& instructions) : mRecentBlockHash(recentBlockHash) diff --git a/src/Solana/Program.cpp b/src/Solana/Program.cpp index d5023c3a66a..f9f196bb209 100644 --- a/src/Solana/Program.cpp +++ b/src/Solana/Program.cpp @@ -27,7 +27,7 @@ Address StakeProgram::addressFromValidatorSeed(const Address& fromAddress, const Address StakeProgram::addressFromRecentBlockhash(const Address& fromAddress, const Data& recentBlockhash, const Address& programId) { Data extended = fromAddress.vector(); - std::string seed = Base58::bitcoin.encode(recentBlockhash); + std::string seed = Base58::encode(recentBlockhash); Data vecSeed(seed.begin(), seed.end()); vecSeed.resize(32); Data additional = programId.vector(); diff --git a/src/Solana/Signature.h b/src/Solana/Signature.h index 8cb169b6e6a..3a1983cbaa9 100644 --- a/src/Solana/Signature.h +++ b/src/Solana/Signature.h @@ -19,7 +19,7 @@ class Signature { std::array bytes; Signature(const std::string& string) { - const auto data = Base58::bitcoin.decode(string); + const auto data = Base58::decode(string); std::copy(data.begin(), data.end(), this->bytes.begin()); } Signature(const std::array& bytes) { this->bytes = bytes; } diff --git a/src/Solana/Signer.cpp b/src/Solana/Signer.cpp index e427bd76401..bab36a4cd76 100644 --- a/src/Solana/Signer.cpp +++ b/src/Solana/Signer.cpp @@ -38,7 +38,7 @@ std::vector
convertReferences(const google::protobuf::RepeatedPtrField< } Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { - auto blockhash = Base58::bitcoin.decode(input.recent_blockhash()); + auto blockhash = Base58::decode(input.recent_blockhash()); auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); LegacyMessage message; std::vector signerKeys; @@ -179,7 +179,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto encoded = transaction.serialize(); protoOutput.set_encoded(encoded); - auto unsignedTx = Base58::bitcoin.encode(transaction.messageData()); + auto unsignedTx = Base58::encode(transaction.messageData()); protoOutput.set_unsigned_tx(unsignedTx.data(), unsignedTx.size()); return protoOutput; diff --git a/src/Solana/Transaction.cpp b/src/Solana/Transaction.cpp index 172779dee30..313f8cc60ac 100644 --- a/src/Solana/Transaction.cpp +++ b/src/Solana/Transaction.cpp @@ -24,7 +24,7 @@ std::string Transaction::serialize() const { } append(buffer, this->messageData()); - return Base58::bitcoin.encode(buffer); + return Base58::encode(buffer); } Data Transaction::messageData() const { diff --git a/src/Solana/VersionedTransaction.cpp b/src/Solana/VersionedTransaction.cpp index 548617f09be..439add21b80 100644 --- a/src/Solana/VersionedTransaction.cpp +++ b/src/Solana/VersionedTransaction.cpp @@ -19,7 +19,7 @@ std::string VersionedTransaction::serialize() const { } append(buffer, this->messageData()); - return Base58::bitcoin.encode(buffer); + return Base58::encode(buffer); } Data VersionedTransaction::messageData() const { diff --git a/src/Tezos/Address.cpp b/src/Tezos/Address.cpp index e351ce4d776..a495096b61a 100644 --- a/src/Tezos/Address.cpp +++ b/src/Tezos/Address.cpp @@ -23,7 +23,7 @@ const std::array tz2Prefix{6, 161, 161}; const std::array tz3Prefix{6, 161, 164}; bool Address::isValid(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string); if (decoded.size() != Address::size) { return false; } @@ -50,7 +50,7 @@ Address::Address(const PublicKey& publicKey) { std::string Address::deriveOriginatedAddress(const std::string& operationHash, int operationIndex) { // Decode and remove 2 byte prefix. - auto decoded = Base58::bitcoin.decodeCheck(operationHash); + auto decoded = Base58::decodeCheck(operationHash); decoded.erase(decoded.begin(), decoded.begin() + 2); TW::encode32BE(operationIndex, decoded); @@ -59,7 +59,7 @@ std::string Address::deriveOriginatedAddress(const std::string& operationHash, i auto prefix = Data({2, 90, 121}); prefix.insert(prefix.end(), hash.begin(), hash.end()); - return Base58::bitcoin.encodeCheck(prefix); + return Base58::encodeCheck(prefix); } Data Address::forge() const { diff --git a/src/Tezos/BinaryCoding.cpp b/src/Tezos/BinaryCoding.cpp index 363ab9ef76b..1b8dd1c481d 100644 --- a/src/Tezos/BinaryCoding.cpp +++ b/src/Tezos/BinaryCoding.cpp @@ -16,7 +16,7 @@ namespace TW::Tezos { std::string base58ToHex(const std::string& string, size_t prefixLength) { - const auto decoded = Base58::bitcoin.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string); if (decoded.size() < prefixLength) { return ""; } @@ -25,7 +25,7 @@ std::string base58ToHex(const std::string& string, size_t prefixLength) { } PublicKey parsePublicKey(const std::string& publicKey) { - const auto decoded = Base58::bitcoin.decodeCheck(publicKey); + const auto decoded = Base58::decodeCheck(publicKey); std::array prefix = {13, 15, 37, 217}; auto pk = Data(); @@ -39,7 +39,7 @@ PublicKey parsePublicKey(const std::string& publicKey) { } PrivateKey parsePrivateKey(const std::string& privateKey) { - const auto decoded = Base58::bitcoin.decodeCheck(privateKey); + const auto decoded = Base58::decodeCheck(privateKey); auto pk = Data(); auto prefix_size = 4ul; diff --git a/src/Tezos/Forging.cpp b/src/Tezos/Forging.cpp index 83088b3f3c0..27bc81f4816 100644 --- a/src/Tezos/Forging.cpp +++ b/src/Tezos/Forging.cpp @@ -18,7 +18,7 @@ namespace { constexpr const char* gTezosContractAddressPrefix{"KT1"}; void encodePrefix(const std::string& address, Data& forged) { - const auto decoded = Base58::bitcoin.decodeCheck(address); + const auto decoded = Base58::decodeCheck(address); constexpr auto prefixSize{3}; forged.insert(forged.end(), decoded.begin() + prefixSize, decoded.end()); } @@ -114,7 +114,7 @@ Data forgePublicKey(PublicKey publicKey) { auto bytes = Data(publicKey.bytes.begin(), publicKey.bytes.end()); append(data, bytes); - auto pk = Base58::bitcoin.encodeCheck(data); + auto pk = Base58::encodeCheck(data); auto decoded = "00" + base58ToHex(pk, 4); return parse_hex(decoded); } diff --git a/src/Tezos/OperationList.cpp b/src/Tezos/OperationList.cpp index fc2e7ed3897..e72c5bf1e53 100644 --- a/src/Tezos/OperationList.cpp +++ b/src/Tezos/OperationList.cpp @@ -21,7 +21,7 @@ void Tezos::OperationList::addOperation(const Operation& operation) { // Forge the given branch to a hex encoded string. Data Tezos::OperationList::forgeBranch() const { std::array prefix = {1, 52}; - const auto decoded = Base58::bitcoin.decodeCheck(branch); + const auto decoded = Base58::decodeCheck(branch); if (decoded.size() != 34 || !std::equal(prefix.begin(), prefix.end(), decoded.begin())) { throw std::invalid_argument("Invalid branch for forge"); } @@ -49,4 +49,4 @@ Data Tezos::OperationList::forge(const PrivateKey& privateKey) const { return forged; } -} // namespace TW::Tezos \ No newline at end of file +} // namespace TW::Tezos diff --git a/src/Tron/Address.cpp b/src/Tron/Address.cpp index a67ea8fd1c2..b24b4806d23 100644 --- a/src/Tron/Address.cpp +++ b/src/Tron/Address.cpp @@ -15,7 +15,7 @@ namespace TW::Tron { bool Address::isValid(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string); if (decoded.size() != Address::size) { return false; } diff --git a/src/Tron/Signer.cpp b/src/Tron/Signer.cpp index 87ed70b30f5..b3278891aea 100644 --- a/src/Tron/Signer.cpp +++ b/src/Tron/Signer.cpp @@ -24,10 +24,10 @@ const std::string TRANSFER_TOKEN_FUNCTION = "0xa9059cbb"; protocol::TransferContract to_internal(const Proto::TransferContract& transfer) { auto internal = protocol::TransferContract(); - const auto ownerAddress = Base58::bitcoin.decodeCheck(transfer.owner_address()); + const auto ownerAddress = Base58::decodeCheck(transfer.owner_address()); internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); - const auto toAddress = Base58::bitcoin.decodeCheck(transfer.to_address()); + const auto toAddress = Base58::decodeCheck(transfer.to_address()); internal.set_to_address(toAddress.data(), toAddress.size()); internal.set_amount(transfer.amount()); @@ -42,10 +42,10 @@ protocol::TransferAssetContract to_internal(const Proto::TransferAssetContract& internal.set_asset_name(transfer.asset_name()); - const auto ownerAddress = Base58::bitcoin.decodeCheck(transfer.owner_address()); + const auto ownerAddress = Base58::decodeCheck(transfer.owner_address()); internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); - const auto toAddress = Base58::bitcoin.decodeCheck(transfer.to_address()); + const auto toAddress = Base58::decodeCheck(transfer.to_address()); internal.set_to_address(toAddress.data(), toAddress.size()); internal.set_amount(transfer.amount()); @@ -56,8 +56,8 @@ protocol::TransferAssetContract to_internal(const Proto::TransferAssetContract& protocol::FreezeBalanceContract to_internal(const Proto::FreezeBalanceContract& freezeContract) { auto internal = protocol::FreezeBalanceContract(); auto resource = protocol::ResourceCode(); - const auto ownerAddress = Base58::bitcoin.decodeCheck(freezeContract.owner_address()); - const auto receiverAddress = Base58::bitcoin.decodeCheck(freezeContract.receiver_address()); + const auto ownerAddress = Base58::decodeCheck(freezeContract.owner_address()); + const auto receiverAddress = Base58::decodeCheck(freezeContract.receiver_address()); protocol::ResourceCode_Parse(freezeContract.resource(), &resource); @@ -73,8 +73,8 @@ protocol::FreezeBalanceContract to_internal(const Proto::FreezeBalanceContract& protocol::UnfreezeBalanceContract to_internal(const Proto::UnfreezeBalanceContract& unfreezeContract) { auto internal = protocol::UnfreezeBalanceContract(); auto resource = protocol::ResourceCode(); - const auto ownerAddress = Base58::bitcoin.decodeCheck(unfreezeContract.owner_address()); - const auto receiverAddress = Base58::bitcoin.decodeCheck(unfreezeContract.receiver_address()); + const auto ownerAddress = Base58::decodeCheck(unfreezeContract.owner_address()); + const auto receiverAddress = Base58::decodeCheck(unfreezeContract.receiver_address()); protocol::ResourceCode_Parse(unfreezeContract.resource(), &resource); @@ -87,7 +87,7 @@ protocol::UnfreezeBalanceContract to_internal(const Proto::UnfreezeBalanceContra protocol::UnfreezeAssetContract to_internal(const Proto::UnfreezeAssetContract& unfreezeContract) { auto internal = protocol::UnfreezeAssetContract(); - const auto ownerAddress = Base58::bitcoin.decodeCheck(unfreezeContract.owner_address()); + const auto ownerAddress = Base58::decodeCheck(unfreezeContract.owner_address()); internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); @@ -96,13 +96,13 @@ protocol::UnfreezeAssetContract to_internal(const Proto::UnfreezeAssetContract& protocol::VoteAssetContract to_internal(const Proto::VoteAssetContract& voteContract) { auto internal = protocol::VoteAssetContract(); - const auto ownerAddress = Base58::bitcoin.decodeCheck(voteContract.owner_address()); + const auto ownerAddress = Base58::decodeCheck(voteContract.owner_address()); internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); internal.set_support(voteContract.support()); internal.set_count(voteContract.count()); for (int i = 0; i < voteContract.vote_address_size(); i++) { - auto voteAddress = Base58::bitcoin.decodeCheck(voteContract.vote_address(i)); + auto voteAddress = Base58::decodeCheck(voteContract.vote_address(i)); internal.add_vote_address(voteAddress.data(), voteAddress.size()); } @@ -111,12 +111,12 @@ protocol::VoteAssetContract to_internal(const Proto::VoteAssetContract& voteCont protocol::VoteWitnessContract to_internal(const Proto::VoteWitnessContract& voteContract) { auto internal = protocol::VoteWitnessContract(); - const auto ownerAddress = Base58::bitcoin.decodeCheck(voteContract.owner_address()); + const auto ownerAddress = Base58::decodeCheck(voteContract.owner_address()); internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); internal.set_support(voteContract.support()); for (int i = 0; i < voteContract.votes_size(); i++) { - auto voteAddress = Base58::bitcoin.decodeCheck(voteContract.votes(i).vote_address()); + auto voteAddress = Base58::decodeCheck(voteContract.votes(i).vote_address()); auto* vote = internal.add_votes(); vote->set_vote_address(voteAddress.data(), voteAddress.size()); @@ -128,7 +128,7 @@ protocol::VoteWitnessContract to_internal(const Proto::VoteWitnessContract& vote protocol::WithdrawBalanceContract to_internal(const Proto::WithdrawBalanceContract& withdrawContract) { auto internal = protocol::WithdrawBalanceContract(); - const auto ownerAddress = Base58::bitcoin.decodeCheck(withdrawContract.owner_address()); + const auto ownerAddress = Base58::decodeCheck(withdrawContract.owner_address()); internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); @@ -137,8 +137,8 @@ protocol::WithdrawBalanceContract to_internal(const Proto::WithdrawBalanceContra protocol::TriggerSmartContract to_internal(const Proto::TriggerSmartContract& triggerSmartContract) { auto internal = protocol::TriggerSmartContract(); - const auto ownerAddress = Base58::bitcoin.decodeCheck(triggerSmartContract.owner_address()); - const auto contractAddress = Base58::bitcoin.decodeCheck(triggerSmartContract.contract_address()); + const auto ownerAddress = Base58::decodeCheck(triggerSmartContract.owner_address()); + const auto contractAddress = Base58::decodeCheck(triggerSmartContract.contract_address()); internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); internal.set_contract_address(contractAddress.data(), contractAddress.size()); @@ -151,7 +151,7 @@ protocol::TriggerSmartContract to_internal(const Proto::TriggerSmartContract& tr } protocol::TriggerSmartContract to_internal(const Proto::TransferTRC20Contract& transferTrc20Contract) { - auto toAddress = Base58::bitcoin.decodeCheck(transferTrc20Contract.to_address()); + auto toAddress = Base58::decodeCheck(transferTrc20Contract.to_address()); // amount is 256 bits, big endian Data amount = data(transferTrc20Contract.amount()); diff --git a/src/Waves/Address.cpp b/src/Waves/Address.cpp index 2f5ad758be7..6103765c48b 100644 --- a/src/Waves/Address.cpp +++ b/src/Waves/Address.cpp @@ -43,12 +43,12 @@ bool Address::isValid(const Data& decoded) { } bool Address::isValid(const std::string& string) { - const auto decoded = Base58::bitcoin.decode(string); + const auto decoded = Base58::decode(string); return isValid(decoded); } Address::Address(const std::string& string) { - const auto decoded = Base58::bitcoin.decode(string); + const auto decoded = Base58::decode(string); if (!isValid(string)) { throw std::invalid_argument("Invalid address key data"); } @@ -79,7 +79,7 @@ Address::Address(const PublicKey &publicKey) { } std::string Address::string() const { - return Base58::bitcoin.encode(bytes); + return Base58::encode(bytes); } } // namespace TW::Waves diff --git a/src/Waves/Transaction.cpp b/src/Waves/Transaction.cpp index 36ac5cb3447..bb6e7885c0d 100644 --- a/src/Waves/Transaction.cpp +++ b/src/Waves/Transaction.cpp @@ -34,13 +34,13 @@ Data serializeTransfer(int64_t amount, std::string asset, int64_t fee, std::stri data.push_back(static_cast(0)); } else { data.push_back(static_cast(1)); - append(data, Base58::bitcoin.decode(asset)); + append(data, Base58::decode(asset)); } if (fee_asset == Transaction::WAVES) { data.push_back(static_cast(0)); } else { data.push_back(static_cast(1)); - append(data, Base58::bitcoin.decode(fee_asset)); + append(data, Base58::decode(fee_asset)); } encode64BE(timestamp, data); encode64BE(amount, data); @@ -86,9 +86,9 @@ json jsonTransfer(const Data& signature, int64_t amount, const std::string& asse jsonTx["type"] = TransactionType::transfer; jsonTx["version"] = TransactionVersion::V2; jsonTx["fee"] = fee; - jsonTx["senderPublicKey"] = Base58::bitcoin.encode(pub_key); + jsonTx["senderPublicKey"] = Base58::encode(pub_key); jsonTx["timestamp"] = timestamp; - jsonTx["proofs"] = json::array({Base58::bitcoin.encode(signature)}); + jsonTx["proofs"] = json::array({Base58::encode(signature)}); jsonTx["recipient"] = Address(to).string(); if (asset != Transaction::WAVES) { jsonTx["assetId"] = asset; @@ -97,7 +97,7 @@ json jsonTransfer(const Data& signature, int64_t amount, const std::string& asse jsonTx["feeAssetId"] = fee_asset; } jsonTx["amount"] = amount; - jsonTx["attachment"] = Base58::bitcoin.encode(attachment); + jsonTx["attachment"] = Base58::encode(attachment); return jsonTx; } @@ -108,9 +108,9 @@ json jsonLease(const Data& signature, int64_t amount, int64_t fee, Address to, i jsonTx["type"] = TransactionType::lease; jsonTx["version"] = TransactionVersion::V2; jsonTx["fee"] = fee; - jsonTx["senderPublicKey"] = Base58::bitcoin.encode(pub_key); + jsonTx["senderPublicKey"] = Base58::encode(pub_key); jsonTx["timestamp"] = timestamp; - jsonTx["proofs"] = json::array({Base58::bitcoin.encode(signature)}); + jsonTx["proofs"] = json::array({Base58::encode(signature)}); jsonTx["recipient"] = Address(to).string(); jsonTx["amount"] = amount; @@ -123,11 +123,11 @@ json jsonCancelLease(const Data& signature, const Data& leaseId, int64_t fee, in jsonTx["type"] = TransactionType::cancelLease; jsonTx["version"] = TransactionVersion::V2; jsonTx["fee"] = fee; - jsonTx["senderPublicKey"] = Base58::bitcoin.encode(pub_key); - jsonTx["leaseId"] = Base58::bitcoin.encode(leaseId); + jsonTx["senderPublicKey"] = Base58::encode(pub_key); + jsonTx["leaseId"] = Base58::encode(leaseId); jsonTx["chainId"] = 87; // mainnet jsonTx["timestamp"] = timestamp; - jsonTx["proofs"] = json::array({Base58::bitcoin.encode(signature)}); + jsonTx["proofs"] = json::array({Base58::encode(signature)}); return jsonTx; } @@ -152,7 +152,7 @@ Data Transaction::serializeToSign() const { return serializeLease(message.amount(), message.fee(), Address(message.to()), input.timestamp(), pub_key); } else if (input.has_cancel_lease_message()) { auto message = input.cancel_lease_message(); - auto leaseId = Base58::bitcoin.decode(message.lease_id()); + auto leaseId = Base58::decode(message.lease_id()); return serializeCancelLease(leaseId, message.fee(), input.timestamp(), pub_key); } @@ -184,7 +184,7 @@ json Transaction::buildJson(const Data& signature) const { pub_key); } else if (input.has_cancel_lease_message()) { auto message = input.cancel_lease_message(); - auto leaseId = Base58::bitcoin.decode(message.lease_id()); + auto leaseId = Base58::decode(message.lease_id()); return jsonCancelLease( signature, leaseId, diff --git a/src/XRP/Address.cpp b/src/XRP/Address.cpp index 2ff0026deba..f675c5afbcd 100644 --- a/src/XRP/Address.cpp +++ b/src/XRP/Address.cpp @@ -11,7 +11,7 @@ namespace TW::Ripple { bool Address::isValid(const std::string& string) { - const auto decoded = Base58::ripple.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string, Base58Alphabet::Ripple); if (decoded.size() != Address::size) { return false; } @@ -19,7 +19,7 @@ bool Address::isValid(const std::string& string) { } Address::Address(const std::string& string) { - const auto decoded = Base58::ripple.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string, Base58Alphabet::Ripple); if (decoded.size() != Address::size) { throw std::invalid_argument("Invalid address string"); } @@ -33,7 +33,7 @@ Address::Address(const PublicKey& publicKey) { } std::string Address::string() const { - return Base58::ripple.encodeCheck(bytes); + return Base58::encodeCheck(bytes, Base58Alphabet::Ripple); } } // namespace TW::Ripple diff --git a/src/XRP/XAddress.cpp b/src/XRP/XAddress.cpp index 0eb5e6978f8..da833880a8f 100644 --- a/src/XRP/XAddress.cpp +++ b/src/XRP/XAddress.cpp @@ -15,7 +15,7 @@ namespace TW::Ripple { const Data prefixMainnet = {0x05, 0x44}; bool XAddress::isValid(const std::string& string) { - const auto decoded = Base58::ripple.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string, Base58Alphabet::Ripple); if (decoded.size() != XAddress::size) { return false; } @@ -32,7 +32,7 @@ XAddress::XAddress(const std::string& string) { if (!XAddress::isValid(string)) { throw std::invalid_argument("Invalid address string"); } - const auto decoded = Base58::ripple.decodeCheck(string); + const auto decoded = Base58::decodeCheck(string, Base58Alphabet::Ripple); 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()); @@ -57,7 +57,7 @@ std::string XAddress::string() const { append(result, byte(flag)); encode32LE(tag, result); append(result, Data{0x00, 0x00, 0x00, 0x00}); - return Base58::ripple.encodeCheck(result); + return Base58::encodeCheck(result, Base58Alphabet::Ripple); } } // namespace TW::Ripple diff --git a/src/interface/TWBase58.cpp b/src/interface/TWBase58.cpp index 923b8a33bf0..e29779ed677 100644 --- a/src/interface/TWBase58.cpp +++ b/src/interface/TWBase58.cpp @@ -14,19 +14,19 @@ using namespace TW; TWString *_Nonnull TWBase58Encode(TWData *_Nonnull data) { const auto& d = *reinterpret_cast(data); - const auto str = Base58::bitcoin.encodeCheck(d); + const auto str = Base58::encodeCheck(d); return TWStringCreateWithUTF8Bytes(str.c_str()); } TWString *_Nonnull TWBase58EncodeNoCheck(TWData *_Nonnull data) { auto& d = *reinterpret_cast(data); - const auto encoded = Base58::bitcoin.encode(d); + const auto encoded = Base58::encode(d); return TWStringCreateWithUTF8Bytes(encoded.c_str()); } TWData *_Nullable TWBase58Decode(TWString *_Nonnull string) { auto& s = *reinterpret_cast(string); - const auto decoded = Base58::bitcoin.decodeCheck(s); + const auto decoded = Base58::decodeCheck(s); if (decoded.empty()) { return nullptr; } @@ -36,7 +36,7 @@ TWData *_Nullable TWBase58Decode(TWString *_Nonnull string) { TWData *_Nullable TWBase58DecodeNoCheck(TWString *_Nonnull string) { auto& s = *reinterpret_cast(string); - const auto decoded = Base58::bitcoin.decode(s); + const auto decoded = Base58::decode(s); if (decoded.empty()) { return nullptr; } diff --git a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp index bdf001a94a8..5970e98caca 100644 --- a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp @@ -1497,7 +1497,7 @@ TEST(BitcoinSigning, EncodeThreeOutput) { TEST(BitcoinSigning, RedeemExtendedPubkeyUTXO) { auto wif = "L4BeKzm3AHDUMkxLRVKTSVxkp6Hz9FcMQPh18YCKU1uioXfovzwP"; - auto decoded = Base58::bitcoin.decodeCheck(wif); + auto decoded = Base58::decodeCheck(wif); auto key = PrivateKey(Data(decoded.begin() + 1, decoded.begin() + 33)); auto pubkey = key.getPublicKey(TWPublicKeyTypeSECP256k1Extended); auto hash = Hash::sha256ripemd(pubkey.bytes.data(), pubkey.bytes.size()); diff --git a/tests/chains/FIO/SignerTests.cpp b/tests/chains/FIO/SignerTests.cpp index 871db8d8091..a55e851f18f 100644 --- a/tests/chains/FIO/SignerTests.cpp +++ b/tests/chains/FIO/SignerTests.cpp @@ -29,7 +29,7 @@ TEST(FIOSigner, SignInternals) { { Data pk2 = parse_hex("80"); append(pk2, pk.bytes); - EXPECT_EQ("5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri", Base58::bitcoin.encodeCheck(pk2)); + EXPECT_EQ("5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri", Base58::encodeCheck(pk2)); } Data rawData = parse_hex("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd774a26285e19fac10ac5390000000001003056372503a85b0000c6eaa6645232017016f2cc12266c6b00000000a8ed3232bd010f6164616d4066696f746573746e657403034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c7877703730643776034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b397300000000000000007016f2cc12266c6b0e726577617264734077616c6c6574000000000000000000000000000000000000000000000000000000000000000000"); Data hash = Hash::sha256(rawData); diff --git a/tests/chains/NEAR/AddressTests.cpp b/tests/chains/NEAR/AddressTests.cpp index 214cd3fab2b..a8acd44cf68 100644 --- a/tests/chains/NEAR/AddressTests.cpp +++ b/tests/chains/NEAR/AddressTests.cpp @@ -32,7 +32,7 @@ TEST(NEARAddress, FromString) { } TEST(NEARAddress, FromPrivateKey) { - auto fullKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto fullKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); auto key = PrivateKey(Data(fullKey.begin(), fullKey.begin() + 32)); auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); auto address = Address(publicKey); diff --git a/tests/chains/NEAR/SerializationTests.cpp b/tests/chains/NEAR/SerializationTests.cpp index 7f6904988b6..98fd6db327b 100644 --- a/tests/chains/NEAR/SerializationTests.cpp +++ b/tests/chains/NEAR/SerializationTests.cpp @@ -15,7 +15,7 @@ namespace TW::NEAR { TEST(NEARSerialization, SerializeTransferTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -28,10 +28,10 @@ TEST(NEARSerialization, SerializeTransferTransaction) { deposit[0] = 1; transfer.set_deposit(deposit.data(), deposit.size()); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); @@ -41,7 +41,7 @@ TEST(NEARSerialization, SerializeTransferTransaction) { } TEST(NEARSerialization, SerializeFunctionCallTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -64,10 +64,10 @@ TEST(NEARSerialization, SerializeFunctionCallTransaction) { args[2] = 3; functionCall.set_args(args.data(), args.size()); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); @@ -77,7 +77,7 @@ TEST(NEARSerialization, SerializeFunctionCallTransaction) { } TEST(NEARSerialization, SerializeStakeTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -94,10 +94,10 @@ TEST(NEARSerialization, SerializeStakeTransaction) { pKey.set_data(publicKey.data(), publicKey.size()); pKey.set_key_type(0); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); @@ -107,7 +107,7 @@ TEST(NEARSerialization, SerializeStakeTransaction) { } TEST(NEARSerialization, SerializeStakeTransaction2) { - auto publicKey = Base58::bitcoin.decode("C2P7YcEmBv31vtCHLBcESteN4Yi4vSCkXEXMTANyB649"); + auto publicKey = Base58::decode("C2P7YcEmBv31vtCHLBcESteN4Yi4vSCkXEXMTANyB649"); auto input = Proto::SigningInput(); input.set_signer_id("vdx.testnet"); @@ -124,10 +124,10 @@ TEST(NEARSerialization, SerializeStakeTransaction2) { pKey.set_data(publicKey.data(), publicKey.size()); pKey.set_key_type(0); - auto blockHash = Base58::bitcoin.decode("ByDnm7c25npQXwNUX5yivbYbpjFcNuNumF6BJjaK3vhJ"); + auto blockHash = Base58::decode("ByDnm7c25npQXwNUX5yivbYbpjFcNuNumF6BJjaK3vhJ"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("5Cfk7QBnmDxxFxQk75FFq4ADrQS9gxHKe6vtuGH6JCCm8WV8aRPEGVqp579JHNmmHMUt49gkCVcH2t7NRnh2v7Qu"); + auto privateKey = Base58::decode("5Cfk7QBnmDxxFxQk75FFq4ADrQS9gxHKe6vtuGH6JCCm8WV8aRPEGVqp579JHNmmHMUt49gkCVcH2t7NRnh2v7Qu"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); @@ -137,7 +137,7 @@ TEST(NEARSerialization, SerializeStakeTransaction2) { } TEST(NEARSerialization, SerializeAddKeyFunctionCallTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -157,10 +157,10 @@ TEST(NEARSerialization, SerializeAddKeyFunctionCallTransaction) { functionCallPermission.set_receiver_id("zzz"); functionCallPermission.add_method_names("www"); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); @@ -170,7 +170,7 @@ TEST(NEARSerialization, SerializeAddKeyFunctionCallTransaction) { } TEST(NEARSerialization, SerializeAddKeyFullAccessTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -189,10 +189,10 @@ TEST(NEARSerialization, SerializeAddKeyFullAccessTransaction) { accessKey.mutable_full_access(); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); @@ -202,7 +202,7 @@ TEST(NEARSerialization, SerializeAddKeyFullAccessTransaction) { } TEST(NEARSerialization, SerializeDeleteKeyTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -216,10 +216,10 @@ TEST(NEARSerialization, SerializeDeleteKeyTransaction) { pKey.set_data(publicKey.data(), publicKey.size()); pKey.set_key_type(0); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); @@ -229,7 +229,7 @@ TEST(NEARSerialization, SerializeDeleteKeyTransaction) { } TEST(NEARSerialization, SerializeCreateAccountTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -239,10 +239,10 @@ TEST(NEARSerialization, SerializeCreateAccountTransaction) { input.add_actions(); input.mutable_actions(0)->mutable_create_account(); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); @@ -252,7 +252,7 @@ TEST(NEARSerialization, SerializeCreateAccountTransaction) { } TEST(NEARSerialization, SerializeDeleteAccountTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -263,10 +263,10 @@ TEST(NEARSerialization, SerializeDeleteAccountTransaction) { auto& deleteAccount = *input.mutable_actions(0)->mutable_delete_account(); deleteAccount.set_beneficiary_id("123"); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto serialized = transactionData(input); diff --git a/tests/chains/NEAR/SignerTests.cpp b/tests/chains/NEAR/SignerTests.cpp index 2a41296484c..84961c45ea8 100644 --- a/tests/chains/NEAR/SignerTests.cpp +++ b/tests/chains/NEAR/SignerTests.cpp @@ -16,7 +16,7 @@ namespace TW::NEAR { TEST(NEARSigner, SignTx) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + auto publicKey = Base58::decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); auto input = Proto::SigningInput(); input.set_signer_id("test.near"); @@ -30,10 +30,10 @@ TEST(NEARSigner, SignTx) { // uint128_t / little endian byte order transfer.set_deposit(deposit.data(), deposit.size()); - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + auto blockHash = Base58::decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); input.set_block_hash(blockHash.data(), blockHash.size()); - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto privateKey = Base58::decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); input.set_private_key(privateKey.data(), 32); auto output = Signer::sign(std::move(input)); diff --git a/tests/chains/NEAR/TWAnySignerTests.cpp b/tests/chains/NEAR/TWAnySignerTests.cpp index 3286c0c8475..b9fa56f75cb 100644 --- a/tests/chains/NEAR/TWAnySignerTests.cpp +++ b/tests/chains/NEAR/TWAnySignerTests.cpp @@ -71,7 +71,7 @@ TEST(TWAnySignerNEAR, SignStake) { } TEST(TWAnySignerNEAR, SignStakeMainnetReplication) { - auto privateKey = Base58::bitcoin.decode("3BPZ9Qu7CviWD4CeKy3DYbNc4suyuBJYnjhVT2oTRCrfb4CQPiTK5tFVdg8Z3ijozxWoxxt9Y1kwkwPntrcc3dom"); + auto privateKey = Base58::decode("3BPZ9Qu7CviWD4CeKy3DYbNc4suyuBJYnjhVT2oTRCrfb4CQPiTK5tFVdg8Z3ijozxWoxxt9Y1kwkwPntrcc3dom"); auto blockHash = parse_hex("e78680996127b7a0f3f2343502e442f24366cba5f79cb72f8bc6d0debb26ce24"); // 0.1 with 24 decimal precision in big endian @@ -95,13 +95,13 @@ TEST(TWAnySignerNEAR, SignStakeMainnetReplication) { ANY_SIGN(input, TWCoinTypeNEAR); // https://explorer.near.org/transactions/kd7ajFw1CfXB8LiJXvhz5NDS7QpQXkuQraAbhb5MMMq - ASSERT_EQ(Base58::bitcoin.encode(data(output.hash())), "kd7ajFw1CfXB8LiJXvhz5NDS7QpQXkuQraAbhb5MMMq"); + ASSERT_EQ(Base58::encode(data(output.hash())), "kd7ajFw1CfXB8LiJXvhz5NDS7QpQXkuQraAbhb5MMMq"); ASSERT_EQ(Base64::encode(data(output.signed_transaction())), "QAAAAGI4ZDVkZjI1MDQ3ODQxMzY1MDA4ZjMwZmI2YjMwZGQ4MjBlOWE4NGQ4NjlmMDU2MjNkMTE0ZTk2ODMxZjJmYmYAzgCT6NK76nb1mB7pToefgkGUHfUe5BKvvr3gW/nq+MgEuu1Mq0YAABEAAABhdmFkby5wb29sdjEubmVhcueGgJlhJ7eg8/I0NQLkQvJDZsul95y3L4vG0N67Js4kAQAAAAIRAAAAZGVwb3NpdF9hbmRfc3Rha2UCAAAAe30A0JjUr3EAAAAAgPZK4ccCLRUAAAAAAAAALNrorr8qTL6u1nlxLpuPa45nFdYmjU96i7CmJP08mVHVzHUaw/bGN30Z3u3o1F2o2yefCBNqO9Ogn9fM25NGCg=="); } TEST(TWAnySignerNEAR, SignUnstakeMainnetReplication) { - auto privateKey = Base58::bitcoin.decode("3BPZ9Qu7CviWD4CeKy3DYbNc4suyuBJYnjhVT2oTRCrfb4CQPiTK5tFVdg8Z3ijozxWoxxt9Y1kwkwPntrcc3dom"); - auto blockHash = Base58::bitcoin.decode("CehJc9uZhqE2m17ZrkqcAog4mxSz6JSvYv1JEK1iBsX9"); + auto privateKey = Base58::decode("3BPZ9Qu7CviWD4CeKy3DYbNc4suyuBJYnjhVT2oTRCrfb4CQPiTK5tFVdg8Z3ijozxWoxxt9Y1kwkwPntrcc3dom"); + auto blockHash = Base58::decode("CehJc9uZhqE2m17ZrkqcAog4mxSz6JSvYv1JEK1iBsX9"); auto amount = parse_hex("00000000000000000000000000000000"); @@ -123,7 +123,7 @@ TEST(TWAnySignerNEAR, SignUnstakeMainnetReplication) { ANY_SIGN(input, TWCoinTypeNEAR); // https://explorer.near.org/transactions/DH6QAX3TkY6XtkteorvKBoGT5hA5ADkURZdzrbbKRs8P - ASSERT_EQ(Base58::bitcoin.encode(data(output.hash())), "DH6QAX3TkY6XtkteorvKBoGT5hA5ADkURZdzrbbKRs8P"); + ASSERT_EQ(Base58::encode(data(output.hash())), "DH6QAX3TkY6XtkteorvKBoGT5hA5ADkURZdzrbbKRs8P"); ASSERT_EQ(Base64::encode(data(output.signed_transaction())), "QAAAAGI4ZDVkZjI1MDQ3ODQxMzY1MDA4ZjMwZmI2YjMwZGQ4MjBlOWE4NGQ4NjlmMDU2MjNkMTE0ZTk2ODMxZjJmYmYAzgCT6NK76nb1mB7pToefgkGUHfUe5BKvvr3gW/nq+MgGuu1Mq0YAABEAAABhdmFkby5wb29sdjEubmVhcq0YnhRlt+TTtagkoy0qKn56zAfGhE+jkTJW6PR5k5r8AQAAAAILAAAAdW5zdGFrZV9hbGwCAAAAe30A0JjUr3EAAAAAAAAAAAAAAAAAAAAAAAAABaFP0EkfJU3VQZ4QAiTwq9ebWDJ7jx7TxbA+VGH4hwKX3gWnmDHVve+LK7/UbbffjF/y8vn0KrPxdh3ONAG0Ag=="); } diff --git a/tests/chains/Nebulas/AddressTests.cpp b/tests/chains/Nebulas/AddressTests.cpp index 45a70df9a09..3ab85ba59a6 100644 --- a/tests/chains/Nebulas/AddressTests.cpp +++ b/tests/chains/Nebulas/AddressTests.cpp @@ -27,7 +27,7 @@ TEST(NebulasAddress, String) { ASSERT_THROW(Address("abc"), std::invalid_argument); ASSERT_EQ(Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY").string(), "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); - ASSERT_EQ(Address(Base58::bitcoin.decode("n1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv")).string(), + ASSERT_EQ(Address(Base58::decode("n1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv")).string(), "n1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv"); const auto address = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); @@ -37,7 +37,7 @@ TEST(NebulasAddress, String) { TEST(NebulasAddress, Data) { Data data; EXPECT_THROW(Address(data).string(), std::invalid_argument); - ASSERT_EQ(Address(Base58::bitcoin.decode("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY")).string(), + ASSERT_EQ(Address(Base58::decode("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY")).string(), "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); } diff --git a/tests/chains/Solana/AddressTests.cpp b/tests/chains/Solana/AddressTests.cpp index 15f011299f9..c5217e3e7c9 100644 --- a/tests/chains/Solana/AddressTests.cpp +++ b/tests/chains/Solana/AddressTests.cpp @@ -19,7 +19,7 @@ namespace TW::Solana::tests { TEST(SolanaAddress, FromPublicKey) { const auto addressString = "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"; - const auto publicKey = PublicKey(Base58::bitcoin.decode(addressString), TWPublicKeyTypeED25519); + const auto publicKey = PublicKey(Base58::decode(addressString), TWPublicKeyTypeED25519); const auto address = Address(publicKey); ASSERT_EQ(addressString, address.string()); } @@ -39,14 +39,14 @@ TEST(SolanaAddress, isValid) { } TEST(SolanaAddress, isValidOnCurve) { - EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("HzqnaMjWFbK2io6WgV2Z5uBguCBU21RMUS16wsDUHkon"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("68io7dTfyeWua1wD1YcCMka4y5iiChceaFRCBjqCM5PK"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("Dra34QLFCjxnk8tUNcBwxs6pgb5spF4oseQYF2xn7ABZ"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_TRUE(PublicKey(Base58::decode("HzqnaMjWFbK2io6WgV2Z5uBguCBU21RMUS16wsDUHkon"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_TRUE(PublicKey(Base58::decode("68io7dTfyeWua1wD1YcCMka4y5iiChceaFRCBjqCM5PK"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_TRUE(PublicKey(Base58::decode("Dra34QLFCjxnk8tUNcBwxs6pgb5spF4oseQYF2xn7ABZ"), TWPublicKeyTypeED25519).isValidED25519()); // negative case - EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("AbygL37RheNZv327cMvZPqKYLLkZ6wqWYexRxgNiZyeP"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_FALSE(PublicKey(Base58::decode("6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_FALSE(PublicKey(Base58::decode("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_FALSE(PublicKey(Base58::decode("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_FALSE(PublicKey(Base58::decode("AbygL37RheNZv327cMvZPqKYLLkZ6wqWYexRxgNiZyeP"), TWPublicKeyTypeED25519).isValidED25519()); } TEST(SolanaAddress, defaultTokenAddress) { diff --git a/tests/chains/Solana/ProgramTests.cpp b/tests/chains/Solana/ProgramTests.cpp index 2b443c6ddfb..a318e462d92 100644 --- a/tests/chains/Solana/ProgramTests.cpp +++ b/tests/chains/Solana/ProgramTests.cpp @@ -27,14 +27,14 @@ TEST(SolanaStakeProgram, addressFromValidatorSeed) { TEST(SolanaStakeProgram, addressFromRecentBlockhash) { { auto user = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Data recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + Data recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto programId = Address("Stake11111111111111111111111111111111111111"); auto expected = Address("GQDDc5EVGJZFC7AvpEJ8eoCQ75Yy4gr7eu17frCjvQRQ"); EXPECT_EQ(StakeProgram::addressFromRecentBlockhash(user, recentBlockhash, programId), expected); } { auto user = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Data recentBlockhash = Base58::bitcoin.decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + Data recentBlockhash = Base58::decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); auto programId = Address("Stake11111111111111111111111111111111111111"); auto expected = Address("2Kos1xJRBq3Ae1GnVNBx7HgJhq8KvdUe2bXE4QGdNaXb"); EXPECT_EQ(StakeProgram::addressFromRecentBlockhash(user, recentBlockhash, programId), expected); @@ -49,9 +49,9 @@ TEST(SolanaTokenProgram, defaultTokenAddress) { TEST(SolanaTokenProgram, findProgramAddress) { std::vector seeds = { - Base58::bitcoin.decode("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"), - Base58::bitcoin.decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), - Base58::bitcoin.decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), + Base58::decode("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"), + Base58::decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), + Base58::decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), }; { Address address = TokenProgram::findProgramAddress(seeds, Address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")); @@ -65,9 +65,9 @@ TEST(SolanaTokenProgram, findProgramAddress) { TEST(SolanaTokenProgram, createProgramAddress) { std::vector seeds4 = { - Base58::bitcoin.decode("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"), - Base58::bitcoin.decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), - Base58::bitcoin.decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), + Base58::decode("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"), + Base58::decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), + Base58::decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), Data{255}}; { Address address = TokenProgram::createProgramAddress(seeds4, Address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")); @@ -89,7 +89,7 @@ TEST(SolanaTokenProgram, createProgramAddress) { EXPECT_EQ(address.string(), "HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds"); } { - std::vector seeds = {Base58::bitcoin.decode("SeedPubey1111111111111111111111111111111111")}; + std::vector seeds = {Base58::decode("SeedPubey1111111111111111111111111111111111")}; Address address = TokenProgram::createProgramAddress(seeds, Address("BPFLoader1111111111111111111111111111111111")); EXPECT_EQ(address.string(), "GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K"); } diff --git a/tests/chains/Solana/SignerTests.cpp b/tests/chains/Solana/SignerTests.cpp index b4ee4910b8e..96bc64a332d 100644 --- a/tests/chains/Solana/SignerTests.cpp +++ b/tests/chains/Solana/SignerTests.cpp @@ -17,17 +17,17 @@ namespace TW::Solana::tests { TEST(SolanaSigner, CompiledInstruction) { const auto privateKey0 = - PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); + PrivateKey(Base58::decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); const auto publicKey0 = privateKey0.getPublicKey(TWPublicKeyTypeED25519); const auto address0 = Address(publicKey0); ASSERT_EQ(Data(publicKey0.bytes.begin(), publicKey0.bytes.end()), - Base58::bitcoin.decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); + Base58::decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); const auto privateKey1 = - PrivateKey(Base58::bitcoin.decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); + PrivateKey(Base58::decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeED25519); const auto address1 = Address(publicKey1); ASSERT_EQ(Data(publicKey1.bytes.begin(), publicKey1.bytes.end()), - Base58::bitcoin.decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); + Base58::decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); Address programId("11111111111111111111111111111111"); std::vector
addresses = {address0, address1, programId}; @@ -85,14 +85,14 @@ TEST(SolanaSigner, CompiledInstructionFindAccount) { TEST(SolanaSigner, SingleSignTransaction) { const auto privateKey = - PrivateKey(Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr")); + PrivateKey(Base58::decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr")); const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::bitcoin.decode("7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q")); + Base58::decode("7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q")); const auto from = Address(publicKey); auto to = Address("EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); std::vector signerKeys; @@ -113,7 +113,7 @@ TEST(SolanaSigner, SingleSignTransaction) { ASSERT_EQ(transaction.serialize(), expectedString); const auto additionalPrivateKey = - PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); + PrivateKey(Base58::decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); signerKeys.push_back(additionalPrivateKey); try { Signer::sign(signerKeys, transaction); @@ -125,14 +125,14 @@ TEST(SolanaSigner, SingleSignTransaction) { TEST(SolanaSigner, SignTransactionToSelf) { const auto privateKey = - PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); + PrivateKey(Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::bitcoin.decode("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu")); + Base58::decode("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu")); const auto from = Address(publicKey); auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); std::vector signerKeys; @@ -154,17 +154,17 @@ TEST(SolanaSigner, SignTransactionToSelf) { TEST(SolanaSigner, MultipleSignTransaction) { const auto privateKey0 = - PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); + PrivateKey(Base58::decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); const auto publicKey0 = privateKey0.getPublicKey(TWPublicKeyTypeED25519); const auto address0 = Address(publicKey0); ASSERT_EQ(Data(publicKey0.bytes.begin(), publicKey0.bytes.end()), - Base58::bitcoin.decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); + Base58::decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); const auto privateKey1 = - PrivateKey(Base58::bitcoin.decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); + PrivateKey(Base58::decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeED25519); const auto address1 = Address(publicKey1); ASSERT_EQ(Data(publicKey1.bytes.begin(), publicKey1.bytes.end()), - Base58::bitcoin.decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); + Base58::decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); Data data = {0, 0, 0, 0}; Address programId("11111111111111111111111111111111"); @@ -177,7 +177,7 @@ TEST(SolanaSigner, MultipleSignTransaction) { MessageHeader header = {2, 0, 1}; std::vector
accountKeys = {address0, address1, programId}; - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); LegacyMessage message; message.header = header; message.accountKeys = accountKeys; @@ -213,21 +213,21 @@ TEST(SolanaSigner, MultipleSignTransaction) { TEST(SolanaSigner, SignUpdateBlockhash) { const auto privateKey = - PrivateKey(Base58::bitcoin.decode("G4VSzrknPBWZ1z2YwUnWTxD1td7wmqR5jMPEJRN6wm8S")); + PrivateKey(Base58::decode("G4VSzrknPBWZ1z2YwUnWTxD1td7wmqR5jMPEJRN6wm8S")); const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::bitcoin.decode("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5")); + Base58::decode("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5")); const auto from = Address(publicKey); auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); std::vector signerKeys; signerKeys.push_back(privateKey); Signer::sign(signerKeys, transaction); - auto newBlockhash = Base58::bitcoin.decode("GgBaCs3NCBuZN12kCJgAW63ydqohFkHEdfdEXBPzLHq"); + auto newBlockhash = Base58::decode("GgBaCs3NCBuZN12kCJgAW63ydqohFkHEdfdEXBPzLHq"); Signer::signUpdateBlockhash(signerKeys, transaction, newBlockhash); std::vector expectedSignatures; @@ -246,10 +246,10 @@ TEST(SolanaSigner, SignUpdateBlockhash) { TEST(SolanaSigner, SignRawMessage) { const auto privateKey = - PrivateKey(Base58::bitcoin.decode("GjXseuD8JavBjKMdd6GEsPYZPV7tMMa46GS2JRS5tHRq")); + PrivateKey(Base58::decode("GjXseuD8JavBjKMdd6GEsPYZPV7tMMa46GS2JRS5tHRq")); const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::bitcoin.decode("3BocAWPm1oNXN5qkAV4QeDUmAPpkTcN1rrmCMWAfsXJY")); + Base58::decode("3BocAWPm1oNXN5qkAV4QeDUmAPpkTcN1rrmCMWAfsXJY")); auto rawMessageData = "01000203207be13c43c4528592eaf3fd34e064c641c5be3cb6691877d7ade94dff36734108eaea30723c33b525" @@ -272,14 +272,14 @@ TEST(SolanaSigner, SignRawMessage) { TEST(SolanaSigner, SignDelegateStakeV2) { const auto privateKeySigner = - PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); + PrivateKey(Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); auto signer = Address(publicKeySigner); ASSERT_EQ(signer.string(), "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); auto programId = Address("Stake11111111111111111111111111111111111111"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); @@ -300,14 +300,14 @@ TEST(SolanaSigner, SignDelegateStakeV2) { TEST(SolanaSigner, SignDelegateStakeV1) { const auto privateKeySigner = - PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); + PrivateKey(Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); auto signer = Address(publicKeySigner); ASSERT_EQ(signer.string(), "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); auto programId = Address("Stake11111111111111111111111111111111111111"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); @@ -328,14 +328,14 @@ TEST(SolanaSigner, SignDelegateStakeV1) { TEST(SolanaSigner, SignCreateTokenAccount) { const auto privateKeySigner = - PrivateKey(Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); + PrivateKey(Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); auto signer = Address(publicKeySigner); EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - auto recentBlockhash = Base58::bitcoin.decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + auto recentBlockhash = Base58::decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); auto message = LegacyMessage::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); auto transaction = VersionedTransaction(VersionedMessage(message)); @@ -365,7 +365,7 @@ TEST(SolanaSigner, SignCreateTokenAccountForOther_3E6UFV) { auto otherMainAddress = Address("3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft"); auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); auto tokenAddress = Address("67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3"); - auto recentBlockhash = Base58::bitcoin.decode("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); + auto recentBlockhash = Base58::decode("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); auto message = LegacyMessage::createTokenCreateAccount(signer, otherMainAddress, token, tokenAddress, recentBlockhash); auto transaction = VersionedTransaction(VersionedMessage(message)); @@ -382,7 +382,7 @@ TEST(SolanaSigner, SignCreateTokenAccountForOther_3E6UFV) { TEST(SolanaSigner, SignTransferToken_3vZ67C) { const auto privateKeySigner = - PrivateKey(Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); + PrivateKey(Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); auto signer = Address(publicKeySigner); EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); @@ -392,7 +392,7 @@ TEST(SolanaSigner, SignTransferToken_3vZ67C) { auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); uint64_t amount = 4000; uint8_t decimals = 6; - auto recentBlockhash = Base58::bitcoin.decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); + auto recentBlockhash = Base58::decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); auto message = LegacyMessage::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); diff --git a/tests/chains/Solana/TWAnySignerTests.cpp b/tests/chains/Solana/TWAnySignerTests.cpp index de5ee2cd869..a96846eba74 100644 --- a/tests/chains/Solana/TWAnySignerTests.cpp +++ b/tests/chains/Solana/TWAnySignerTests.cpp @@ -25,7 +25,7 @@ const auto expectedString1 = "sW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM"; TEST(TWAnySignerSolana, SignTransfer) { - auto privateKey = Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); + auto privateKey = Base58::decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); auto input = Proto::SigningInput(); auto& message = *input.mutable_transfer_transaction(); @@ -60,7 +60,7 @@ TEST(TWAnySignerSolana, SignV0Transfer) { } TEST(TWAnySignerSolana, SignTransferToSelf) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Proto::SigningInput(); auto& message = *input.mutable_transfer_transaction(); @@ -80,7 +80,7 @@ TEST(TWAnySignerSolana, SignTransferToSelf) { } TEST(TWAnySignerSolana, SignTransferWithMemoAndReference) { - const auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + const auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_transfer_transaction(); @@ -100,7 +100,7 @@ TEST(TWAnySignerSolana, SignTransferWithMemoAndReference) { } TEST(TWAnySignerSolana, SignDelegateStakeTransaction_noStakeAccount) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_delegate_stake_transaction(); @@ -118,7 +118,7 @@ TEST(TWAnySignerSolana, SignDelegateStakeTransaction_noStakeAccount) { } TEST(TWAnySignerSolana, SignDelegateStakeTransaction_withAccount) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_delegate_stake_transaction(); @@ -136,7 +136,7 @@ TEST(TWAnySignerSolana, SignDelegateStakeTransaction_withAccount) { } TEST(TWAnySignerSolana, SignDeactivateStakeTransaction) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_deactivate_stake_transaction(); @@ -152,7 +152,7 @@ TEST(TWAnySignerSolana, SignDeactivateStakeTransaction) { } TEST(TWAnySignerSolana, SignDeactivateAllStakeTransaction) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_deactivate_all_stake_transaction(); @@ -169,7 +169,7 @@ TEST(TWAnySignerSolana, SignDeactivateAllStakeTransaction) { } TEST(TWAnySignerSolana, SignWithdrawStakeTransaction) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_withdraw_transaction(); @@ -186,7 +186,7 @@ TEST(TWAnySignerSolana, SignWithdrawStakeTransaction) { } TEST(TWAnySignerSolana, SignWithdrawAllStakeTransaction) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_withdraw_all_transaction(); @@ -207,7 +207,7 @@ TEST(TWAnySignerSolana, SignWithdrawAllStakeTransaction) { } TEST(TWAnySignerSolana, SignDeactivateStakeTransaction_1) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_deactivate_stake_transaction(); @@ -228,7 +228,7 @@ TEST(TWAnySignerSolana, SignDeactivateStakeTransaction_1) { } TEST(TWAnySignerSolana, SignWithdrawStakeTransaction_1) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); auto input = Solana::Proto::SigningInput(); auto& message = *input.mutable_withdraw_transaction(); @@ -245,7 +245,7 @@ TEST(TWAnySignerSolana, SignWithdrawStakeTransaction_1) { } TEST(TWAnySignerSolana, SignCreateTokenAccount1) { - auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); + auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); auto input = Solana::Proto::SigningInput(); @@ -307,7 +307,7 @@ TEST(TWAnySignerSolana, SignCreateTokenAccountForOther_3E6UFV) { } TEST(TWAnySignerSolana, SignTokenTransfer1_3vZ67C) { - auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); + auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); auto input = Solana::Proto::SigningInput(); @@ -330,7 +330,7 @@ TEST(TWAnySignerSolana, SignTokenTransfer1_3vZ67C) { } TEST(TWAnySignerSolana, SignTokenTransfer2_2pMvzp) { - auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); + auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); auto input = Solana::Proto::SigningInput(); @@ -353,7 +353,7 @@ TEST(TWAnySignerSolana, SignTokenTransfer2_2pMvzp) { } TEST(TWAnySignerSolana, SignCreateAndTransferToken_449VaY) { - auto privateKeyData = Base58::bitcoin.decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); + auto privateKeyData = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); auto input = Solana::Proto::SigningInput(); @@ -377,7 +377,7 @@ TEST(TWAnySignerSolana, SignCreateAndTransferToken_449VaY) { } TEST(TWAnySignerSolana, SignCreateAndTransferTokenWithMemoReferences) { - const auto privateKeyData = Base58::bitcoin.decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); + const auto privateKeyData = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); EXPECT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); auto input = Solana::Proto::SigningInput(); @@ -403,7 +403,7 @@ TEST(TWAnySignerSolana, SignCreateAndTransferTokenWithMemoReferences) { TEST(TWAnySignerSolana, SignJSON) { auto json = STRING(R"({"recentBlockhash":"11111111111111111111111111111111","transferTransaction":{"recipient":"EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd","value":"42"}})"); - Data keyData = Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); + Data keyData = Base58::decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); EXPECT_EQ(hex(keyData), "8778cc93c6596387e751d2dc693bbd93e434bd233bc5b68a826c56131821cb63"); auto key = WRAPD(TWDataCreateWithBytes(keyData.data(), keyData.size())); diff --git a/tests/chains/Solana/TransactionTests.cpp b/tests/chains/Solana/TransactionTests.cpp index 0558f31605e..25bed86af71 100644 --- a/tests/chains/Solana/TransactionTests.cpp +++ b/tests/chains/Solana/TransactionTests.cpp @@ -18,7 +18,7 @@ namespace TW::Solana { TEST(SolanaTransaction, TransferMessageData) { auto from = Address("6eoo7i1khGhVm8tLBMAdq4ax2FxkKP4G7mCcfHyr3STN"); auto to = Address("56B334QvCDMSirsmtEJGfanZm8GqeQarrSjdAb2MbeNM"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto transaction = Transaction(from, to, 42, recentBlockhash); auto expectedHex = @@ -32,7 +32,7 @@ TEST(SolanaTransaction, TransferMessageData) { TEST(SolanaTransaction, TransferSerializeTransaction) { auto from = Address("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5"); auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto transaction = Transaction(from, to, 42, recentBlockhash); Signature signature( "46SRiQGvtPb1iivDfnuC3dW1GzXkfQPTjdUyvFqF2sdPvFrsfx94fys2xpNKR6UiAj7RgKWdJG6mEfe85up6i1JT"); @@ -50,7 +50,7 @@ TEST(SolanaTransaction, TransferSerializeTransaction) { TEST(SolanaTransaction, TransferTransactionPayToSelf) { auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto transaction = Transaction(from, to, 42, recentBlockhash); Signature signature( "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); @@ -67,7 +67,7 @@ TEST(SolanaTransaction, TransferTransactionPayToSelf) { TEST(SolanaTransaction, TransferWithMemoAndReferenceTransaction) { const auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); const auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); const auto memo = "HelloSolana73"; std::vector
references = {Address("GaeTAQZyhVEocTC7iY8GztSyY5cBAJTkAUUA1kLFLMV")}; auto transaction = Transaction(from, to, 42, recentBlockhash, memo, references); @@ -83,7 +83,7 @@ TEST(SolanaTransaction, StakeSerializeTransactionV2) { auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); auto programId = Address("Stake11111111111111111111111111111111111111"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); auto transaction = Transaction(message); @@ -100,7 +100,7 @@ TEST(SolanaTransaction, StakeSerializeTransactionV1) { auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); auto programId = Address("Stake11111111111111111111111111111111111111"); - auto recentBlockhash = Base58::bitcoin.decode("11111111111111111111111111111111"); + auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); auto transaction = Transaction(message); @@ -117,7 +117,7 @@ TEST(SolanaTransaction, CreateTokenAccountTransaction) { auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - auto recentBlockhash = Base58::bitcoin.decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + auto recentBlockhash = Base58::decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); auto message = LegacyMessage::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); EXPECT_EQ(message.header.numRequiredSignatures, 1); EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); @@ -130,7 +130,7 @@ TEST(SolanaTransaction, CreateTokenAccountTransaction) { EXPECT_EQ(message.accountKeys[4].string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); EXPECT_EQ(message.accountKeys[5].string(), "SysvarRent111111111111111111111111111111111"); EXPECT_EQ(message.accountKeys[6].string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - EXPECT_EQ(Base58::bitcoin.encode(message.mRecentBlockHash), "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + EXPECT_EQ(Base58::encode(message.mRecentBlockHash), "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); ASSERT_EQ(message.instructions.size(), 1ul); EXPECT_EQ(message.instructions[0].programId.string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); ASSERT_EQ(message.instructions[0].accounts.size(), 7ul); @@ -159,7 +159,7 @@ TEST(SolanaTransaction, TransferTokenTransaction_3vZ67C) { auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); uint64_t amount = 4000; uint8_t decimals = 6; - auto recentBlockhash = Base58::bitcoin.decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); + auto recentBlockhash = Base58::decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); auto message = LegacyMessage::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); EXPECT_EQ(message.header.numRequiredSignatures, 1); EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); diff --git a/tests/chains/Tezos/SignerTests.cpp b/tests/chains/Tezos/SignerTests.cpp index eb024a36721..d11504003cc 100644 --- a/tests/chains/Tezos/SignerTests.cpp +++ b/tests/chains/Tezos/SignerTests.cpp @@ -78,7 +78,7 @@ TEST(TezosSigner, SignOperationList) { op_list.addOperation(delegateOperation); - auto decodedPrivateKey = Base58::bitcoin.decodeCheck("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); + auto decodedPrivateKey = Base58::decodeCheck("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); auto key = PrivateKey(Data(decodedPrivateKey.begin() + 4, decodedPrivateKey.end())); std::string expectedForgedBytesToSign = hex(op_list.forge(key)); diff --git a/tests/chains/Waves/TWAnySignerTests.cpp b/tests/chains/Waves/TWAnySignerTests.cpp index c814761a4f9..213fffd7771 100644 --- a/tests/chains/Waves/TWAnySignerTests.cpp +++ b/tests/chains/Waves/TWAnySignerTests.cpp @@ -15,7 +15,7 @@ namespace TW::Waves::tests { TEST(TWAnySignerWaves, Sign) { auto input = Proto::SigningInput(); - const auto privateKey = Base58::bitcoin.decode("83mqJpmgB5Mko1567sVAdqZxVKsT6jccXt3eFSi4G1zE"); + const auto privateKey = Base58::decode("83mqJpmgB5Mko1567sVAdqZxVKsT6jccXt3eFSi4G1zE"); input.set_timestamp(int64_t(1559146613)); input.set_private_key(privateKey.data(), privateKey.size()); From 4c40f20449c34835b74106a5fdc6e2fb3278e12b Mon Sep 17 00:00:00 2001 From: hewigovens <360470+hewigovens@users.noreply.github.com> Date: Wed, 22 Feb 2023 22:17:03 +0900 Subject: [PATCH 108/426] Update name for ThunderCore (#2946) --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- .../core/app/blockchains/TestCoinType.kt | 2 +- docs/registry.md | 2 +- include/TrustWalletCore/TWCoinType.h | 2 +- registry.json | 2 +- swift/Tests/Blockchains/RippleTests.swift | 2 +- swift/Tests/CoinAddressDerivationTests.swift | 2 +- swift/Tests/CoinTypeTests.swift | 2 +- tests/chains/ThunderToken/TWCoinTypeTests.cpp | 20 +++++++++---------- tests/common/CoinAddressDerivationTests.cpp | 2 +- tests/interface/TWCoinTypeTests.cpp | 4 ++-- tests/interface/TWHRPTests.cpp | 2 +- 12 files changed, 22 insertions(+), 22 deletions(-) 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 a5958c59d15..706f0745abe 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 @@ -53,7 +53,7 @@ class CoinAddressDerivationTests { POANETWORK -> assertEquals("0xe8a3e8bE17E172B6926130eAfB521e9D2849aca9", address) XRP -> assertEquals("rPwE3gChNKtZ1mhH3Ko8YFGqKmGRWLWXV3", address) TEZOS -> assertEquals("tz1acnY9VbMagps26Kj3RfoGRWD9nYG5qaRX", address) - THUNDERTOKEN -> assertEquals("0x4b92b3ED6d8b24575Bf5ce4C6a86ED261DA0C8d7", address) + THUNDERCORE -> assertEquals("0x4b92b3ED6d8b24575Bf5ce4C6a86ED261DA0C8d7", address) TOMOCHAIN -> assertEquals("0xC74b6D8897cBa9A4b659d43fEF73C9cA852cE424", address) TRON -> assertEquals("TQ5NMqJjhpQGK7YJbESKtNCo86PJ89ujio", address) VECHAIN -> assertEquals("0x1a553275dF34195eAf23942CB7328AcF9d48c160", address) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt index f8fc2f3a5bf..21349ebfa3c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt @@ -21,7 +21,7 @@ class TestCoinType { assertEquals(CoinType.LITECOIN.value(), 2) assertEquals(CoinType.TRON.value(), 195) assertEquals(CoinType.ETHEREUM.value(), 60) - assertEquals(CoinType.THUNDERTOKEN.value(), 1001) + assertEquals(CoinType.THUNDERCORE.value(), 1001) assertEquals(CoinType.WANCHAIN.value(), 5718350) assertEquals(CoinType.CALLISTO.value(), 820) assertEquals(CoinType.ETHEREUMCLASSIC.value(), 61) diff --git a/docs/registry.md b/docs/registry.md index e1a73222d10..afe488db265 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -64,7 +64,7 @@ This list is generated from [./registry.json](../registry.json) | 931 | THORChain | RUNE | | | | 966 | Polygon | MATIC | | | | 996 | OKX Chain | OKT | | | -| 1001 | Thunder Token | TT | | | +| 1001 | ThunderCore | TT | | | | 1023 | Harmony | ONE | | | | 1024 | Ontology | ONT | | | | 1729 | Tezos | XTZ | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 0337ee4c6b0..879b64de7fd 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -68,7 +68,7 @@ enum TWCoinType { TWCoinTypeStellar = 148, TWCoinTypeTezos = 1729, TWCoinTypeTheta = 500, - TWCoinTypeThunderToken = 1001, + TWCoinTypeThunderCore = 1001, TWCoinTypeNEO = 888, TWCoinTypeTomoChain = 889, TWCoinTypeTron = 195, diff --git a/registry.json b/registry.json index 24f21c446ac..d592ba5c615 100644 --- a/registry.json +++ b/registry.json @@ -1574,7 +1574,7 @@ }, { "id": "thundertoken", - "name": "Thunder Token", + "name": "ThunderCore", "coinId": 1001, "symbol": "TT", "decimals": 18, diff --git a/swift/Tests/Blockchains/RippleTests.swift b/swift/Tests/Blockchains/RippleTests.swift index c2e96a65781..0582d8df579 100644 --- a/swift/Tests/Blockchains/RippleTests.swift +++ b/swift/Tests/Blockchains/RippleTests.swift @@ -55,7 +55,7 @@ class RippleTests: XCTestCase { $0.lastLedgerSequence = 32268269 $0.account = "rfxdLwsZnoespnTDDb1Xhvbc8EFNdztaoq" $0.privateKey = Data(hexString: "a5576c0f63da10e584568c8d134569ff44017b0a249eb70657127ae04f38cc77")! - $0.opPayment = operation + $0.opPayment = operation2 } let output2: RippleSigningOutput = AnySigner.sign(input: input2, coin: .xrp) XCTAssertEqual(output2.encoded, output.encoded) diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index d641e7150d6..3677452c53c 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -190,7 +190,7 @@ class CoinAddressDerivationTests: XCTestCase { case .theta: let expectedResult = "0x0d1fa20c218Fec2f2C55d52aB267940485fa5DA4" assertCoinDerivation(coin, expectedResult, derivedAddress, address) - case .thunderToken: + case .thunderCore: let expectedResult = "0x4b92b3ED6d8b24575Bf5ce4C6a86ED261DA0C8d7" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .tomoChain: diff --git a/swift/Tests/CoinTypeTests.swift b/swift/Tests/CoinTypeTests.swift index 353b85cf729..8884a0fe0eb 100644 --- a/swift/Tests/CoinTypeTests.swift +++ b/swift/Tests/CoinTypeTests.swift @@ -14,7 +14,7 @@ class CoinTypeTests: XCTestCase { XCTAssertEqual(CoinType.litecoin.rawValue, 2) XCTAssertEqual(CoinType.tron.rawValue, 195) XCTAssertEqual(CoinType.ethereum.rawValue, 60) - XCTAssertEqual(CoinType.thunderToken.rawValue, 1001) + XCTAssertEqual(CoinType.thunderCore.rawValue, 1001) XCTAssertEqual(CoinType.wanchain.rawValue, 5718350) XCTAssertEqual(CoinType.callisto.rawValue, 820) XCTAssertEqual(CoinType.ethereumClassic.rawValue, 61) diff --git a/tests/chains/ThunderToken/TWCoinTypeTests.cpp b/tests/chains/ThunderToken/TWCoinTypeTests.cpp index c001ea07218..1820f72d1ea 100644 --- a/tests/chains/ThunderToken/TWCoinTypeTests.cpp +++ b/tests/chains/ThunderToken/TWCoinTypeTests.cpp @@ -14,21 +14,21 @@ TEST(TWThunderTokenCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeThunderToken)); + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeThunderCore)); auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeThunderToken, txId.get())); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeThunderCore, txId.get())); auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeThunderToken, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeThunderToken)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeThunderToken)); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeThunderCore, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeThunderCore)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeThunderCore)); - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeThunderToken), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeThunderToken)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeThunderToken)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeThunderToken)); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeThunderCore), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeThunderCore)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeThunderCore)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeThunderCore)); assertStringsEqual(symbol, "TT"); assertStringsEqual(txUrl, "https://scan.thundercore.com/transactions/t123"); assertStringsEqual(accUrl, "https://scan.thundercore.com/address/a12"); assertStringsEqual(id, "thundertoken"); - assertStringsEqual(name, "Thunder Token"); + assertStringsEqual(name, "ThunderCore"); } diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 3a506f452fd..b5bc432d8d4 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -65,7 +65,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeSmartChain: case TWCoinTypeSmartChainLegacy: case TWCoinTypeTheta: - case TWCoinTypeThunderToken: + case TWCoinTypeThunderCore: case TWCoinTypeTomoChain: case TWCoinTypeVeChain: case TWCoinTypeWanchain: diff --git a/tests/interface/TWCoinTypeTests.cpp b/tests/interface/TWCoinTypeTests.cpp index cb3ac95c1c9..d5c31585372 100644 --- a/tests/interface/TWCoinTypeTests.cpp +++ b/tests/interface/TWCoinTypeTests.cpp @@ -55,7 +55,7 @@ TEST(TWCoinType, TWPurpose) { ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeStellar)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTezos)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTheta)); - ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeThunderToken)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeThunderCore)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTomoChain)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTron)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeVeChain)); @@ -127,7 +127,7 @@ TEST(TWCoinType, TWPublicKeyType) { ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeStellar)); ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeTezos)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTheta)); - ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeThunderToken)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeThunderCore)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTomoChain)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTron)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeVeChain)); diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index 7946c79c9e5..616938fca6a 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -112,7 +112,7 @@ TEST(TWHPR, HPRByCoinType) { ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeStellar)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeTezos)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeTheta)); - ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeThunderToken)); + ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeThunderCore)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeTomoChain)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeTron)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeVeChain)); From 714bfaca8b9fa800fa4e5bcc957fc8e5697fbd76 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 24 Feb 2023 23:22:03 +0100 Subject: [PATCH 109/426] [Tezos]: Blind signing support (#2956) * feat(tezos): blind signing support * refactoring encoded op name and type * Add manual step Co-authored-by: hewigovens <360470+hewigovens@users.noreply.github.com> --- src/Tezos/Signer.cpp | 16 ++++++++----- src/proto/Tezos.proto | 5 +++- swift/Tests/Blockchains/TezosTests.swift | 30 ++++++++++++++++++++++++ tests/chains/Tezos/TWAnySignerTests.cpp | 15 ++++++++++++ 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/Tezos/Signer.cpp b/src/Tezos/Signer.cpp index 248df7fd115..a8e8b4f0cfc 100644 --- a/src/Tezos/Signer.cpp +++ b/src/Tezos/Signer.cpp @@ -18,14 +18,18 @@ using namespace TW; namespace TW::Tezos { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { - auto operationList = Tezos::OperationList(input.operation_list().branch()); - for (Proto::Operation operation : input.operation_list().operations()) { - operationList.addOperation(operation); - } - auto signer = Signer(); PrivateKey key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - Data encoded = signer.signOperationList(key, operationList); + Data encoded; + if (input.encoded_operations().empty()) { + auto operationList = Tezos::OperationList(input.operation_list().branch()); + for (Proto::Operation operation : input.operation_list().operations()) { + operationList.addOperation(operation); + } + encoded = signer.signOperationList(key, operationList); + } else { + encoded = signer.signData(key, TW::data(input.encoded_operations())); + } auto output = Proto::SigningOutput(); output.set_encoded(encoded.data(), encoded.size()); diff --git a/src/proto/Tezos.proto b/src/proto/Tezos.proto index 7a56c4d2ab5..8732c2e1974 100644 --- a/src/proto/Tezos.proto +++ b/src/proto/Tezos.proto @@ -9,8 +9,11 @@ message SigningInput { // One or more operations OperationList operation_list = 1; + // Encoded operation bytes obtained with $RPC_URL/chains/main/blocks/head/helpers/forge/operations, operation_list will be ignored. + bytes encoded_operations = 2; + // The secret private key used for signing (32 bytes). - bytes private_key = 2; + bytes private_key = 3; } // Result containing the signed and encoded transaction. diff --git a/swift/Tests/Blockchains/TezosTests.swift b/swift/Tests/Blockchains/TezosTests.swift index 67a26a6c971..a6076a3beaa 100644 --- a/swift/Tests/Blockchains/TezosTests.swift +++ b/swift/Tests/Blockchains/TezosTests.swift @@ -161,4 +161,34 @@ class TezosTests: XCTestCase { XCTAssertEqual(output.encoded.hexString, expected) } + + public func testSignEncodedBytes() throws { + + let key = Data(hexString: "3caf5afaed067890cd850efd1555df351aa482badb4a541c29261f1acf261bf5")! + let bytes = Data(hexString: "64aa7792af40de41371a72b3342daa7bf3d2b5a84511e9074341fdd52148dd9d6c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542850f96c3a1079d780080ade2040155959998da7e79231e2be8ed8ff373ac1b1574b000ffff04737761700000009e070703060707020000000807070508030b000007070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a4438457907070080dac409070700bdf892a1a291e196aa0503066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0497c3a107f10f180001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f76650000002d070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900006c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0498c3a107f70f090001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f766500000036070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900bdf892a1a291e196aa056c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542e71599c3a107fabb01400001b1f0d7affc39861f7f5c75f917f683d2e9f55e3100ffff04737761700000009a070700000707000007070001070700bdf892a1a291e196aa05070700a3f683c2a6d80a07070100000018323032332d30322d32345431333a34303a32322e3332385a070705090100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484805090100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a443845796c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049ac3a107f50f1b000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050507070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a52717572617400006c00ad756cb46ba6f59efa8bd10ff544ba9d20d0954285109bc3a107a0820100000155959998da7e79231e2be8ed8ff373ac1b1574b000ffff0473776170000000a1070703060707020000000807070508030b000807070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a44384579070700a3f683c2a6d80a070700a4f096bfbe9df6f0e00603066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049cc3a107ed0f00000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050807070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a5271757261740000")! + let input = TezosSigningInput.with { + $0.privateKey = key + $0.encodedOperations = bytes + } + + let output: TezosSigningOutput = AnySigner.sign(input: input, coin: .tezos) + + let expected = "64aa7792af40de41371a72b3342daa7bf3d2b5a84511e9074341fdd52148dd9d6c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542850f96c3a1079d780080ade2040155959998da7e79231e2be8ed8ff373ac1b1574b000ffff04737761700000009e070703060707020000000807070508030b000007070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a4438457907070080dac409070700bdf892a1a291e196aa0503066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0497c3a107f10f180001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f76650000002d070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900006c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0498c3a107f70f090001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f766500000036070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900bdf892a1a291e196aa056c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542e71599c3a107fabb01400001b1f0d7affc39861f7f5c75f917f683d2e9f55e3100ffff04737761700000009a070700000707000007070001070700bdf892a1a291e196aa05070700a3f683c2a6d80a07070100000018323032332d30322d32345431333a34303a32322e3332385a070705090100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484805090100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a443845796c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049ac3a107f50f1b000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050507070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a52717572617400006c00ad756cb46ba6f59efa8bd10ff544ba9d20d0954285109bc3a107a0820100000155959998da7e79231e2be8ed8ff373ac1b1574b000ffff0473776170000000a1070703060707020000000807070508030b000807070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a44384579070700a3f683c2a6d80a070700a4f096bfbe9df6f0e00603066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049cc3a107ed0f00000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050807070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a5271757261740000e10077fc3068aaaf1c7779e1dc2c396b3b40d73ddda04648bf4b16ac2e747c89b461771488e80da3aa30fc18c90de99fd358bfb76683f3c3ec250b1ee09b6d07" + + XCTAssertEqual(output.encoded.hexString, expected) + + // How to do it without AnySigner + var watermark = Data([0x03]) + watermark.append(bytes) + + let hash = Hash.blake2b(data: watermark, size: 32) + let privateKey = PrivateKey(data: key)! + let signature = privateKey.sign(digest: hash, curve: .ed25519)! + + var signed = Data() + signed.append(bytes) + signed.append(signature) + + XCTAssertEqual(signed.hexString, expected) + } } diff --git a/tests/chains/Tezos/TWAnySignerTests.cpp b/tests/chains/Tezos/TWAnySignerTests.cpp index 89fac461677..92a080ba8d9 100644 --- a/tests/chains/Tezos/TWAnySignerTests.cpp +++ b/tests/chains/Tezos/TWAnySignerTests.cpp @@ -79,6 +79,21 @@ TEST(TWAnySignerTezos, SignFA2) { ASSERT_EQ(hex(output.encoded()), "1b1f9345dc9f77bd24b09034d1d2f9a28f02ac837f49db54b8d68341f53dc4b76c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0695d8b601a08d0600000136767f88850bae28bfb9f46b73c5e87ede4de12700ffff087472616e7366657200000066020000006107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b5550020000003107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070000000a552d24710d6c59383286700c6c2917b25a6c1fa8b587e593c289dd47704278796792f1e522c1623845ec991e292b0935445e6994850bd03f035a006c5ed93806"); } +TEST(TWAnySignerTezos, BlindSign) { + // Successfully broadcasted: https://ghostnet.tzkt.io/oobGgTkDNz9eqGVXiU4wShPZydkroCrmbKjoDcfSqhnM7GmcdEu/15229334 + auto key = parse_hex("3caf5afaed067890cd850efd1555df351aa482badb4a541c29261f1acf261bf5"); + auto bytes = parse_hex("64aa7792af40de41371a72b3342daa7bf3d2b5a84511e9074341fdd52148dd9d6c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542850f96c3a1079d780080ade2040155959998da7e79231e2be8ed8ff373ac1b1574b000ffff04737761700000009e070703060707020000000807070508030b000007070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a4438457907070080dac409070700bdf892a1a291e196aa0503066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0497c3a107f10f180001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f76650000002d070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900006c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0498c3a107f70f090001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f766500000036070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900bdf892a1a291e196aa056c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542e71599c3a107fabb01400001b1f0d7affc39861f7f5c75f917f683d2e9f55e3100ffff04737761700000009a070700000707000007070001070700bdf892a1a291e196aa05070700a3f683c2a6d80a07070100000018323032332d30322d32345431333a34303a32322e3332385a070705090100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484805090100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a443845796c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049ac3a107f50f1b000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050507070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a52717572617400006c00ad756cb46ba6f59efa8bd10ff544ba9d20d0954285109bc3a107a0820100000155959998da7e79231e2be8ed8ff373ac1b1574b000ffff0473776170000000a1070703060707020000000807070508030b000807070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a44384579070700a3f683c2a6d80a070700a4f096bfbe9df6f0e00603066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049cc3a107ed0f00000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050807070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a5271757261740000"); + + Proto::SigningInput input; + input.set_private_key(key.data(), key.size()); + input.set_encoded_operations(bytes.data(), bytes.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeTezos); + + EXPECT_EQ(hex(output.encoded()), "64aa7792af40de41371a72b3342daa7bf3d2b5a84511e9074341fdd52148dd9d6c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542850f96c3a1079d780080ade2040155959998da7e79231e2be8ed8ff373ac1b1574b000ffff04737761700000009e070703060707020000000807070508030b000007070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a4438457907070080dac409070700bdf892a1a291e196aa0503066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0497c3a107f10f180001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f76650000002d070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900006c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0498c3a107f70f090001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f766500000036070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900bdf892a1a291e196aa056c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542e71599c3a107fabb01400001b1f0d7affc39861f7f5c75f917f683d2e9f55e3100ffff04737761700000009a070700000707000007070001070700bdf892a1a291e196aa05070700a3f683c2a6d80a07070100000018323032332d30322d32345431333a34303a32322e3332385a070705090100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484805090100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a443845796c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049ac3a107f50f1b000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050507070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a52717572617400006c00ad756cb46ba6f59efa8bd10ff544ba9d20d0954285109bc3a107a0820100000155959998da7e79231e2be8ed8ff373ac1b1574b000ffff0473776170000000a1070703060707020000000807070508030b000807070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a44384579070700a3f683c2a6d80a070700a4f096bfbe9df6f0e00603066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049cc3a107ed0f00000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050807070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a5271757261740000e10077fc3068aaaf1c7779e1dc2c396b3b40d73ddda04648bf4b16ac2e747c89b461771488e80da3aa30fc18c90de99fd358bfb76683f3c3ec250b1ee09b6d07"); +} + TEST(TWAnySignerTezos, Sign) { auto key = parse_hex("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f"); auto revealKey = parse_hex("311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95"); From a077d3fb2312f495eae68c2bfddfd5b8762ed6e4 Mon Sep 17 00:00:00 2001 From: Maxim Pestryakov Date: Thu, 2 Mar 2023 13:39:00 +0800 Subject: [PATCH 110/426] [Kotlin] Fix JS types (#2965) --- codegen/bin/codegen | 1 + codegen/lib/code_generator.rb | 8 +- codegen/lib/kotlin_helper.rb | 81 +++++++++++++----- .../templates/kotlin/js_accessors_class.erb | 21 +++++ .../templates/kotlin/js_accessors_enum.erb | 34 ++++++++ .../templates/kotlin/js_accessors_struct.erb | 14 +++ codegen/lib/templates/kotlin/js_class.erb | 9 +- codegen/lib/templates/kotlin/js_enum.erb | 12 +-- codegen/lib/templates/kotlin_js_accessors.erb | 7 ++ include/TrustWalletCore/TWAccount.h | 15 ++-- jni/kotlin/AnySigner.c | 8 +- jni/kotlin/AnySigner.h | 8 +- .../convention.proto-generation.gradle.kts | 2 +- kotlin/gradle/libs.versions.toml | 6 +- kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 61574 -> 61608 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- kotlin/gradlew | 4 +- kotlin/wallet-core-kotlin/build.gradle.kts | 2 - .../kotlin/com/trustwallet/core/AnySigner.kt | 15 +++- .../kotlin/com/trustwallet/core/AnySigner.kt | 23 ++--- .../kotlin/com/trustwallet/core/AnySigner.kt | 19 ++-- .../src/jsMain/kotlin/WalletCore.kt | 23 +++-- .../kotlin/com/trustwallet/core/AnySigner.kt | 23 +++-- .../kotlin/com/trustwallet/core/ByteArray.kt | 24 ++++++ .../com/trustwallet/core/JsAnySigner.kt | 20 +++++ .../core/{UInt8Array.kt => JsWalletCore.kt} | 14 ++- src/interface/TWAccount.cpp | 11 +-- 27 files changed, 288 insertions(+), 118 deletions(-) create mode 100644 codegen/lib/templates/kotlin/js_accessors_class.erb create mode 100644 codegen/lib/templates/kotlin/js_accessors_enum.erb create mode 100644 codegen/lib/templates/kotlin/js_accessors_struct.erb create mode 100644 codegen/lib/templates/kotlin_js_accessors.erb create mode 100644 kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt create mode 100644 kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt rename kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/{UInt8Array.kt => JsWalletCore.kt} (53%) diff --git a/codegen/bin/codegen b/codegen/bin/codegen index 10dfda8d9ff..5cd99179bb8 100755 --- a/codegen/bin/codegen +++ b/codegen/bin/codegen @@ -97,6 +97,7 @@ if options.kotlin generator.render_kotlin_android generator.render_kotlin_ios generator.render_kotlin_js + generator.render_kotlin_js_accessors generator.render_kotlin_jni_h generator.render_kotlin_jni_c end diff --git a/codegen/lib/code_generator.rb b/codegen/lib/code_generator.rb index d870418b222..0cac71f7870 100644 --- a/codegen/lib/code_generator.rb +++ b/codegen/lib/code_generator.rb @@ -49,7 +49,7 @@ def render_swift_enum_template(file:, header:, template:, output_subfolder:, ext end # Renders a template - def render_template(header:, template:, output_subfolder:, extension:) + def render_template(header:, template:, output_subfolder:, extension:, file_prefix: "") FileUtils.mkdir_p File.join(output_folder, output_subfolder) @entities.zip(files) do |entity, file| # Make current entity available to templates @@ -65,7 +65,7 @@ def render_template(header:, template:, output_subfolder:, extension:) code << "\n" unless header.nil? code << string - path = File.expand_path(File.join(output_folder, output_subfolder, "#{file}.#{extension}")) + path = File.expand_path(File.join(output_folder, output_subfolder, "#{file_prefix}#{file}.#{extension}")) File.write(path, code) end end @@ -121,6 +121,10 @@ def render_kotlin_js render_template(header: nil, template: 'kotlin_js.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/jsMain/generated/com/trustwallet/core', extension: 'kt') end + def render_kotlin_js_accessors + render_template(header: nil, template: 'kotlin_js_accessors.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/jsMain/generated/com/trustwallet/core', extension: 'kt', file_prefix: "Js") + end + def render_kotlin_jni_h render_template(header: 'copyright_header.erb', template: 'kotlin_jni_h.erb', output_subfolder: 'kotlin/wallet-core-kotlin/src/androidMain/cpp/generated', extension: 'h') end diff --git a/codegen/lib/kotlin_helper.rb b/codegen/lib/kotlin_helper.rb index 0d072ad3637..cc465817ea6 100644 --- a/codegen/lib/kotlin_helper.rb +++ b/codegen/lib/kotlin_helper.rb @@ -20,6 +20,14 @@ def self.parameters(params) names.join(', ') end + def self.js_parameters(params) + names = params.map do |param| + name = fix_name(param.name) + "#{name}: #{js_type(param.type)}" + end + names.join(', ') + end + def self.calling_parameters_ios(params) names = params.map do |param| name = fix_name(param.name) @@ -76,18 +84,24 @@ def self.convert_calling_type_ios(t) def self.convert_calling_type_js(t) case t.name when :data - "#{if t.is_nullable then '?' else '' end}.toUInt8Array()" + "#{if t.is_nullable then '?' else '' end}.asUInt8Array()" + when :uint8 + ".toByte()" + when :uint16 + ".toShort()" + when :uint32 + ".toInt()" when :uint64 - ".toUInt()" + ".toInt()" when :int64 ".toInt()" when :size - ".toUInt()" + ".toInt()" else if t.is_enum - "#{if t.is_nullable then '?' else '' end}._value" + "#{if t.is_nullable then '?' else '' end}.jsValue" elsif t.is_class - "#{if t.is_nullable then '?' else '' end}._value" + "#{if t.is_nullable then '?' else '' end}.jsValue" else '' end @@ -121,38 +135,38 @@ def self.convert_calling_return_type_js(t, expression = '') when :void expression when :data - "#{expression}.unsafeCast()#{nullable}.toByteArray()" + "#{expression}#{nullable}.asByteArray()" when :int - "#{expression}.unsafeCast().toInt()" + "#{expression}.toInt()" when :uint8 - "#{expression}.unsafeCast().toByte().toUByte()" + "#{expression}.toByte().toUByte()" when :uint16 - "#{expression}.unsafeCast().toShort().toUShort()" + "#{expression}.toShort().toUShort()" when :uint32 - "#{expression}.unsafeCast().toInt().toUInt()" + "#{expression}.toInt().toUInt()" when :uint64 - "#{expression}.unsafeCast().toLong().toULong()" + "#{expression}.toLong().toULong()" when :int8 - "#{expression}.unsafeCast().toByte()" + "#{expression}.toByte()" when :int16 - "#{expression}.unsafeCast().toShort()" + "#{expression}.toShort()" when :int32 - "#{expression}.unsafeCast().toInt()" + "#{expression}.toInt()" when :int64 - "#{expression}.unsafeCast().toLong()" + "#{expression}.toLong()" when :size - "#{expression}.unsafeCast().toLong().toULong()" + "#{expression}.toLong().toULong()" else if t.is_enum - "#{t.name}.fromValue(#{expression})#{if t.is_nullable then '' else '!!' end}" + "#{t.name}.fromValue(#{expression})" elsif t.is_class if t.is_nullable - "#{expression}.unsafeCast()?.let { #{t.name}(it, Unit) }" + "#{expression}?.let { #{t.name}(it) }" else - "#{t.name}(#{expression}, Unit)" + "#{t.name}(#{expression})" end else - "#{expression} as #{type(t)}" + expression end end end @@ -208,4 +222,31 @@ def self.return_type(t) end end + def self.js_type(t, is_constructor = false) + nullable = "#{if t.is_nullable && !is_constructor then '?' else '' end}" + case t.name + when :void + "" + when :bool + "Boolean#{nullable}" + when :int, :uint8, :int8, :uint16, :int16, :uint32, :int32, :uint64, :int64, :size + "Number#{nullable}" + when :data + "UInt8Array#{nullable}" + when :string + "String#{nullable}" + else + "Js#{t.name}#{nullable}" + end + end + + def self.js_return_type(t, is_constructor = false) + case t.name + when :void + "" + else + ": #{js_type(t, is_constructor)}" + end + end + end diff --git a/codegen/lib/templates/kotlin/js_accessors_class.erb b/codegen/lib/templates/kotlin/js_accessors_class.erb new file mode 100644 index 00000000000..9930f021865 --- /dev/null +++ b/codegen/lib/templates/kotlin/js_accessors_class.erb @@ -0,0 +1,21 @@ +<%= render('kotlin/package.erb') %> + +@JsModule("@trustwallet/wallet-core") +@JsName("<%= entity.name %>") +external class Js<%= entity.name %> { +<%- entity.properties.each do |property| -%> + fun <%= KotlinHelper.fix_name(WasmCppHelper.format_name(property.name)) %>()<%= KotlinHelper.js_return_type(property.return_type) %> +<%- end -%> +<% entity.methods.each do |method| -%> +<% next if method.name == "Delete" -%> + fun <%= KotlinHelper.fix_name(WasmCppHelper.format_name(method.name)) %>(<%= KotlinHelper.js_parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.js_return_type(method.return_type) %> +<% end -%> + companion object { +<% entity.static_methods.each do |method| -%> + fun <%= KotlinHelper.fix_name(WasmCppHelper.function_name(entity: entity, function: method)) %>(<%= KotlinHelper.js_parameters(method.parameters) %>)<%= KotlinHelper.js_return_type(method.return_type, method.name.start_with?("Create")) %> +<% end -%> + } +} + +inline val JsWalletCore.<%= entity.name %>: Js<%= entity.name %>.Companion + get() = asDynamic().<%= entity.name %>.unsafeCast.Companion>() diff --git a/codegen/lib/templates/kotlin/js_accessors_enum.erb b/codegen/lib/templates/kotlin/js_accessors_enum.erb new file mode 100644 index 00000000000..34e44a7b352 --- /dev/null +++ b/codegen/lib/templates/kotlin/js_accessors_enum.erb @@ -0,0 +1,34 @@ +<%= render('kotlin/package.erb') %> + +@JsModule("@trustwallet/wallet-core") +@JsName("<%= entity.name %>") +external class Js<%= entity.name %> { + val value: Number + companion object { +<% entity.cases.each do |c| -%> + val <%= KotlinHelper.fix_name(WasmCppHelper.format_name(c.name)) %>: Js<%= entity.name %> +<% end -%> + } +} + +inline val JsWalletCore.<%= entity.name %>: Js<%= entity.name %>.Companion + get() = asDynamic().<%= entity.name %>.unsafeCast.Companion>() + +<% if entity.properties.any? || entity.methods.any? -%> +@JsModule("@trustwallet/wallet-core") +@JsName("<%= entity.name %>Ext") +external object Js<%= entity.name %>Ext { +<%# Static method declarations -%> +<%- entity.properties.each do |property| -%> + fun <%= KotlinHelper.fix_name(WasmCppHelper.format_name(property.name)) %>(<%= KotlinHelper.js_parameters(property.parameters) %>)<%= KotlinHelper.js_return_type(property.return_type) %> +<%- end -%> +<% entity.methods.each do |method| -%> +<% next if method.name.start_with?('Create') -%> + fun <%= KotlinHelper.fix_name(WasmCppHelper.function_name(entity: entity, function: method)) %>(<%= KotlinHelper.js_parameters(method.parameters) %>)<%= KotlinHelper.js_return_type(method.return_type) %> +<% end -%> +} + +inline val JsWalletCore.<%= entity.name %>Ext: Js<%= entity.name %>Ext + get() = asDynamic().<%= entity.name %>Ext.unsafeCastExt>() + +<% end -%> \ No newline at end of file diff --git a/codegen/lib/templates/kotlin/js_accessors_struct.erb b/codegen/lib/templates/kotlin/js_accessors_struct.erb new file mode 100644 index 00000000000..671f438c880 --- /dev/null +++ b/codegen/lib/templates/kotlin/js_accessors_struct.erb @@ -0,0 +1,14 @@ +<%= render('kotlin/package.erb') %> + +@JsModule("@trustwallet/wallet-core") +@JsName("<%= entity.name %>") +external object Js<%= entity.name %> { +<%# Static method declarations -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') -%> + fun <%= KotlinHelper.fix_name(WasmCppHelper.function_name(entity: entity, function: method)) %>(<%= KotlinHelper.js_parameters(method.parameters) %>)<%= KotlinHelper.js_return_type(method.return_type) %> +<% end -%> +} + +inline val JsWalletCore.<%= entity.name %>: Js<%= entity.name %> + get() = asDynamic().<%= entity.name %>.unsafeCast>() diff --git a/codegen/lib/templates/kotlin/js_class.erb b/codegen/lib/templates/kotlin/js_class.erb index 187cbbd940b..5f6ad4809d7 100644 --- a/codegen/lib/templates/kotlin/js_class.erb +++ b/codegen/lib/templates/kotlin/js_class.erb @@ -4,26 +4,25 @@ <% methods = entity.methods.select { |method| not method.name.start_with?('Delete') } -%> <% static_methods = entity.static_methods.select { |method| not method.name.start_with?('Create') } -%> actual class <%= entity.name %> constructor( - val _value: dynamic, - unit: Unit, // Hack + val jsValue: Js<%= entity.name %>, ) { <%# Constructors -%> <%- constructors.each do |constructor| -%> actual constructor(<%= KotlinHelper.parameters(constructor.parameters) %>) : - this(WalletCore.Instance.<%= entity.name %>.<%= WasmCppHelper.function_name(entity: entity, function: constructor) %>(<%= KotlinHelper.calling_parameters_js(constructor.parameters) %>), Unit) + this(WalletCore.Instance.<%= entity.name %>.<%= WasmCppHelper.function_name(entity: entity, function: constructor) %>(<%= KotlinHelper.calling_parameters_js(constructor.parameters) %>)) <% end -%> <%# Property declarations -%> <% entity.properties.each do |property| -%> actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> - get() = <%= KotlinHelper.convert_calling_return_type_js(property.return_type, "_value.#{WasmCppHelper.function_name(entity: entity, function: property)}()") %> + get() = <%= KotlinHelper.convert_calling_return_type_js(property.return_type, "jsValue.#{WasmCppHelper.function_name(entity: entity, function: property)}()") %> <% end -%> <%# Method declarations -%> <% methods.each do |method| -%> actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> = - <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "_value.#{WasmCppHelper.function_name(entity: entity, function: method)}(#{KotlinHelper.calling_parameters_js(method.parameters.drop(1))})") %> + <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "jsValue.#{WasmCppHelper.function_name(entity: entity, function: method)}(#{KotlinHelper.calling_parameters_js(method.parameters.drop(1))})") %> <% end -%> <% if entity.static_properties.any? || static_methods.any? -%> diff --git a/codegen/lib/templates/kotlin/js_enum.erb b/codegen/lib/templates/kotlin/js_enum.erb index 4c5895d77ac..4ce79c684fc 100644 --- a/codegen/lib/templates/kotlin/js_enum.erb +++ b/codegen/lib/templates/kotlin/js_enum.erb @@ -1,7 +1,7 @@ <%= render('kotlin/package.erb') %> actual enum class <%= entity.name %>( - internal val _value: dynamic, + val jsValue: Js<%= entity.name %>, ) { <%# Cases -%> <% entity.cases.each_with_index do |c, i| -%> @@ -12,21 +12,21 @@ actual enum class <%= entity.name %>( <%- entity.properties.each do |property| -%> actual val <%= KotlinHelper.format_name(property.name) %><%= KotlinHelper.return_type(property.return_type) %> - get() = <%= KotlinHelper.convert_calling_return_type_js(property.return_type, "WalletCore.Instance.#{entity.name}Ext.#{WasmCppHelper.function_name(entity: entity, function: property)}(_value)") %> + get() = <%= KotlinHelper.convert_calling_return_type_js(property.return_type, "WalletCore.Instance.#{entity.name}Ext.#{WasmCppHelper.function_name(entity: entity, function: property)}(jsValue)") %> <%- end -%> <%# Method declarations -%> <%- entity.methods.each do |method| -%> <%- next if method.name.start_with?('Delete') -%> actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> = - <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "WalletCore.Instance.#{entity.name}Ext.#{WasmCppHelper.function_name(entity: entity, function: method)}(_value#{', ' if not method.parameters.one?}#{KotlinHelper.calling_parameters_js(method.parameters.drop(1))})") %> + <%= KotlinHelper.convert_calling_return_type_js(method.return_type, "WalletCore.Instance.#{entity.name}Ext.#{WasmCppHelper.function_name(entity: entity, function: method)}(jsValue#{', ' if not method.parameters.one?}#{KotlinHelper.calling_parameters_js(method.parameters.drop(1))})") %> <%- end -%> <%# Value -%> <% if entity.cases.any? { |e| !e.value.nil? } -%> - internal companion object { - internal fun fromValue(value: dynamic): <%= entity.name %>? = - values().firstOrNull { it._value == value } + companion object { + fun fromValue(jsValue: Js<%= entity.name %>): <%= entity.name %> = + values().first { it.jsValue.value == jsValue.value } } <% end -%> } diff --git a/codegen/lib/templates/kotlin_js_accessors.erb b/codegen/lib/templates/kotlin_js_accessors.erb new file mode 100644 index 00000000000..63dda74b3b2 --- /dev/null +++ b/codegen/lib/templates/kotlin_js_accessors.erb @@ -0,0 +1,7 @@ +<%- if entity.is_a?(EnumDecl) -%> +<%= render('kotlin/js_accessors_enum.erb') -%> +<%- elsif entity.is_struct -%> +<%= render('kotlin/js_accessors_struct.erb') -%> +<%- else -%> +<%= render('kotlin/js_accessors_class.erb') -%> +<%- end -%> diff --git a/include/TrustWalletCore/TWAccount.h b/include/TrustWalletCore/TWAccount.h index c5d6f5e33ce..20a11cf7d6f 100644 --- a/include/TrustWalletCore/TWAccount.h +++ b/include/TrustWalletCore/TWAccount.h @@ -28,7 +28,8 @@ struct TWAccount; /// \param extendedPublicKey Base58 encoded extended public key. /// \return A new Account. TW_EXPORT_STATIC_METHOD -struct TWAccount* _Nonnull TWAccountCreate(TWString* _Nonnull address, enum TWCoinType coin, +struct TWAccount* _Nonnull TWAccountCreate(TWString* _Nonnull address, + enum TWCoinType coin, enum TWDerivation derivation, TWString* _Nonnull derivationPath, TWString* _Nonnull publicKey, @@ -45,6 +46,12 @@ void TWAccountDelete(struct TWAccount *_Nonnull account); TW_EXPORT_PROPERTY TWString *_Nonnull TWAccountAddress(struct TWAccount *_Nonnull account); +/// Return CoinType enum of an account. +/// +/// \param account Account to get the coin type of. +TW_EXPORT_PROPERTY +enum TWCoinType TWAccountCoin(struct TWAccount* _Nonnull account); + /// Returns the derivation enum of an account. /// /// \param account Account to get the derivation enum of. @@ -69,10 +76,4 @@ TWString* _Nonnull TWAccountPublicKey(struct TWAccount* _Nonnull account); TW_EXPORT_PROPERTY TWString* _Nonnull TWAccountExtendedPublicKey(struct TWAccount* _Nonnull account); -/// Return CoinType enum of an account. -/// -/// \param account Account to get the coin type of. -TW_EXPORT_PROPERTY -enum TWCoinType TWAccountCoin(struct TWAccount* _Nonnull account); - TW_EXTERN_C_END diff --git a/jni/kotlin/AnySigner.c b/jni/kotlin/AnySigner.c index 8e0c9f389f8..57c215eb34c 100644 --- a/jni/kotlin/AnySigner.c +++ b/jni/kotlin/AnySigner.c @@ -11,7 +11,7 @@ #include "AnySigner.h" #include "TWJNI.h" -jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_signImpl(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin) { +jbyteArray JNICALL Java_com_trustwallet_core_AnySigner_sign(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin) { jclass coinClass = (*env)->GetObjectClass(env, coin); jmethodID coinValueMethodID = (*env)->GetMethodID(env, coinClass, "value", "()I"); uint32_t coinValue = (*env)->CallIntMethod(env, coin, coinValueMethodID); @@ -23,14 +23,14 @@ jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_signImpl(JNIEnv *env, j return resultData; } -jboolean JNICALL Java_com_trustwallet_core_AnySignerKt_supportsJsonImpl(JNIEnv *env, jclass thisClass, jobject coin) { +jboolean JNICALL Java_com_trustwallet_core_AnySigner_supportsJson(JNIEnv *env, jclass thisClass, jobject coin) { jclass coinClass = (*env)->GetObjectClass(env, coin); jmethodID coinValueMethodID = (*env)->GetMethodID(env, coinClass, "value", "()I"); uint32_t coinValue = (*env)->CallIntMethod(env, coin, coinValueMethodID); return TWAnySignerSupportsJSON(coinValue); } -jstring JNICALL Java_com_trustwallet_core_AnySignerKt_signJsonImpl(JNIEnv *env, jclass thisClass, jstring json, jbyteArray key, jobject coin) { +jstring JNICALL Java_com_trustwallet_core_AnySigner_signJson(JNIEnv *env, jclass thisClass, jstring json, jbyteArray key, jobject coin) { jclass coinClass = (*env)->GetObjectClass(env, coin); jmethodID coinValueMethodID = (*env)->GetMethodID(env, coinClass, "value", "()I"); uint32_t coinValue = (*env)->CallIntMethod(env, coin, coinValueMethodID); @@ -43,7 +43,7 @@ jstring JNICALL Java_com_trustwallet_core_AnySignerKt_signJsonImpl(JNIEnv *env, return TWStringJString(result, env); } -jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_planImpl(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin) { +jbyteArray JNICALL Java_com_trustwallet_core_AnySigner_plan(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin) { jclass coinClass = (*env)->GetObjectClass(env, coin); jmethodID coinValueMethodID = (*env)->GetMethodID(env, coinClass, "value", "()I"); uint32_t coinValue = (*env)->CallIntMethod(env, coin, coinValueMethodID); diff --git a/jni/kotlin/AnySigner.h b/jni/kotlin/AnySigner.h index 18f98104f62..05742d57122 100644 --- a/jni/kotlin/AnySigner.h +++ b/jni/kotlin/AnySigner.h @@ -13,16 +13,16 @@ TW_EXTERN_C_BEGIN JNIEXPORT -jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_signImpl(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin); +jbyteArray JNICALL Java_com_trustwallet_core_AnySigner_sign(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin); JNIEXPORT -jboolean JNICALL Java_com_trustwallet_core_AnySignerKt_supportsJsonImpl(JNIEnv *env, jclass thisClass, jobject coin); +jboolean JNICALL Java_com_trustwallet_core_AnySigner_supportsJson(JNIEnv *env, jclass thisClass, jobject coin); JNIEXPORT -jstring JNICALL Java_com_trustwallet_core_AnySignerKt_signJsonImpl(JNIEnv *env, jclass thisClass, jstring json, jbyteArray key, jobject coin); +jstring JNICALL Java_com_trustwallet_core_AnySigner_signJson(JNIEnv *env, jclass thisClass, jstring json, jbyteArray key, jobject coin); JNIEXPORT -jbyteArray JNICALL Java_com_trustwallet_core_AnySignerKt_planImpl(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin); +jbyteArray JNICALL Java_com_trustwallet_core_AnySigner_plan(JNIEnv *env, jclass thisClass, jbyteArray input, jobject coin); TW_EXTERN_C_END diff --git a/kotlin/build-logic/src/main/kotlin/convention.proto-generation.gradle.kts b/kotlin/build-logic/src/main/kotlin/convention.proto-generation.gradle.kts index 7775d61e4ed..112de933e3a 100644 --- a/kotlin/build-logic/src/main/kotlin/convention.proto-generation.gradle.kts +++ b/kotlin/build-logic/src/main/kotlin/convention.proto-generation.gradle.kts @@ -18,7 +18,7 @@ val copyProtoTask = task("copyProtos") { .listFiles { file -> file.extension == "proto" } .orEmpty() .forEach { file -> - val packageName = file.nameWithoutExtension.toLowerCase() + val packageName = file.nameWithoutExtension.lowercase() file .readText() .replaceFirst( diff --git a/kotlin/gradle/libs.versions.toml b/kotlin/gradle/libs.versions.toml index 012e8c6ba34..701f94e473b 100644 --- a/kotlin/gradle/libs.versions.toml +++ b/kotlin/gradle/libs.versions.toml @@ -1,13 +1,13 @@ [versions] -android-sdk-tools = "33.0.1" +android-sdk-tools = "33.0.2" android-sdk-min = "24" android-sdk-compile = "33" android-cmake = "3.22.1" android-ndk = "25.2.9519653" kotlin = "1.8.10" -agp = "7.4.1" -wire = "4.4.3" +agp = "7.4.2" +wire = "4.5.1" [libraries] wire-runtime = { module = "com.squareup.wire:wire-runtime", version.ref = "wire" } diff --git a/kotlin/gradle/wrapper/gradle-wrapper.jar b/kotlin/gradle/wrapper/gradle-wrapper.jar index 943f0cbfa754578e88a3dae77fce6e3dea56edbf..ccebba7710deaf9f98673a68957ea02138b60d0a 100644 GIT binary patch delta 5094 zcmZu#c|6qH|DG9RA4`noBZNWrC2N)tSqjO%%aX0^O4dPAB*iC6_9R<`apl^#h-_oY z)(k_0v8Fxp{fyi9-uwN%e)GpU&v~BrS>~KG^PF=MNmQjIDr&QHR7f-kM{%U_u*1=5 zGC}ae5(^Rrg9QY8$x^}oiJ0d2O9YW{J~$dD1ovlvh&0B4L)!4S=z;Hac>K{#9q9cKq;>>BtKo1!+gw`yqE zSK8x^jC|B!qmSW#uyb@T^CkB9qRd{N3V-rEi}AEgoU_J27lw_0X`}c0&m9JhxM;RK z54_gdZ(u?R5`B3}NeVal2NTHqlktM`2eTF28%6BZCWW$-shf0l-BOVSm)hU58MTPy zDcY-5777j;ccU!Yba8wH=X6OdPJ8O5Kp^3gUNo>!b=xb6T2F&LiC2eBJj8KuLPW!4 zw3V^NnAKZm^D?tmliCvzi>UtoDH%V#%SM0d*NS+m%4}qO<)M1E{OpQ(v&ZNc`vdi| zEGlVi$Dgxy1p6+k0qGLQt(JwxZxLCZ4>wJ=sb0v%Ki?*+!ic_2exumn{%Co|| z-axdK#RUC;P|vqbe?L`K!j;sUo=uuR_#ZkRvBf%Txo6{OL&I(?dz?47Z(DcX3KTw> zGY%A=kX;fBkq$F^sX|-)1Qkg##+n-Ci{qJVPj@P?l_1Y`nD^v>fZ3HMX%(4p-TlD(>yWwJij!6Jw}l7h>CIm@Ou5B@$Wy`Ky*814%Mdi1GfG1zDG9NogaoVHHr4gannv4?w6g&10!j=lKM zFW;@=Z0}vAPAxA=R4)|`J??*$|Fh`5=ks*V7TapX`+=4n*{aXxRhh-EGX_Xrzjb4r zn0vO7Cc~wtyeM_8{**~9y7>+}1JV8Buhg%*hy|PUc#!vw#W(HFTL|BpM)U0>JxG6S zLnqn1!0++RyyJ>5VU<4mDv8>Q#{EtgS3mj7Hx}Zkr0tz1}h8Kn6q`MiwC z{Y#;D!-ndlImST(C@(*i5f0U(jD29G7g#nkiPX zki6M$QYX_fNH=E4_eg9*FFZ3wF9YAKC}CP89Kl(GNS(Ag994)0$OL4-fj_1EdR}ARB#-vP_$bWF`Qk58+ z4Jq*-YkcmCuo9U%oxGeYe7Be=?n}pX+x>ob(8oPLDUPiIryT8v*N4@0{s_VYALi;lzj19ivLJKaXt7~UfU|mu9zjbhPnIhG2`uI34urWWA9IO{ z_1zJ)lwSs{qt3*UnD}3qB^kcRZ?``>IDn>qp8L96bRaZH)Zl`!neewt(wjSk1i#zf zb8_{x_{WRBm9+0CF4+nE)NRe6K8d|wOWN)&-3jCDiK5mj>77=s+TonlH5j`nb@rB5 z5NX?Z1dk`E#$BF{`(D>zISrMo4&}^wmUIyYL-$PWmEEfEn-U0tx_vy$H6|+ zi{ytv2@JXBsot|%I5s74>W1K{-cvj0BYdNiRJz*&jrV9>ZXYZhEMULcM=fCmxkN&l zEoi=)b)Vazc5TQC&Q$oEZETy@!`Gnj`qoXl7mcwdY@3a-!SpS2Mau|uK#++@>H8QC zr2ld8;<_8We%@E?S=E?=e9c$BL^9X?bj*4W;<+B&OOe+3{<`6~*fC(=`TO>o^A(Y! zA`Qc1ky?*6xjVfR?ugE~oY`Gtzhw^{Z@E6vZ`mMRAp>Odpa!m zzWmtjT|Lj^qiZMfj%%un-o$Eu>*v12qF{$kCKai^?DF=$^tfyV%m9;W@pm-BZn_6b z{jsXY3!U`%9hzk6n7YyHY%48NhjI6jjuUn?Xfxe0`ARD_Q+T_QBZ{ zUK@!63_Wr`%9q_rh`N4=J=m;v>T{Y=ZLKN^m?(KZQ2J%|3`hV0iogMHJ} zY6&-nXirq$Yhh*CHY&Qf*b@@>LPTMf z(cMorwW?M11RN{H#~ApKT)F!;R#fBHahZGhmy>Sox`rk>>q&Y)RG$-QwH$_TWk^hS zTq2TC+D-cB21|$g4D=@T`-ATtJ?C=aXS4Q}^`~XjiIRszCB^cvW0OHe5;e~9D%D10 zl4yP4O=s-~HbL7*4>#W52eiG7*^Hi)?@-#*7C^X5@kGwK+paI>_a2qxtW zU=xV7>QQROWQqVfPcJ$4GSx`Y23Z&qnS?N;%mjHL*EVg3pBT{V7bQUI60jtBTS?i~ zycZ4xqJ<*3FSC6_^*6f)N|sgB5Bep(^%)$=0cczl>j&n~KR!7WC|3;Zoh_^GuOzRP zo2Hxf50w9?_4Qe368fZ0=J|fR*jO_EwFB1I^g~i)roB|KWKf49-)!N%Ggb%w=kB8)(+_%kE~G!(73aF=yCmM3Cfb9lV$G!b zoDIxqY{dH>`SILGHEJwq%rwh46_i`wkZS-NY95qdNE)O*y^+k#JlTEij8NT(Y_J!W zFd+YFoZB|auOz~A@A{V*c)o7E(a=wHvb@8g5PnVJ&7D+Fp8ABV z5`&LD-<$jPy{-y*V^SqM)9!#_Pj2-x{m$z+9Z*o|JTBGgXYYVM;g|VbitDUfnVn$o zO)6?CZcDklDoODzj+ti@i#WcqPoZ!|IPB98LW!$-p+a4xBVM@%GEGZKmNjQMhh)zv z7D){Gpe-Dv=~>c9f|1vANF&boD=Nb1Dv>4~eD636Lldh?#zD5{6JlcR_b*C_Enw&~ z5l2(w(`{+01xb1FCRfD2ap$u(h1U1B6e&8tQrnC}Cy0GR=i^Uue26Rc6Dx}!4#K*0 zaxt`a+px7-Z!^(U1WN2#kdN#OeR|2z+C@b@w+L67VEi&ZpAdg+8`HJT=wIMJqibhT ztb3PFzsq&7jzQuod3xp7uL?h-7rYao&0MiT_Bux;U*N#ebGv92o(jM2?`1!N2W_M* zeo9$%hEtIy;=`8z1c|kL&ZPn0y`N)i$Y1R9>K!el{moiy)014448YC#9=K zwO3weN|8!`5bU_#f(+ZrVd*9`7Uw?!q?yo&7sk&DJ;#-^tcCtqt5*A(V;&LdHq7Hg zI6sC@!ly9p$^@v&XDsgIuv;9#w^!C1n5+10-tEw~ZdO1kqMDYyDl!5__o}f3hYe2M zCeO)~m&&=JZn%cVH3HzPlcE`9^@``2u+!Y}Remn)DLMHc-h5A9ATgs;7F7=u2=vBlDRbjeYvyNby=TvpI{5nb2@J_YTEEEj4q<@zaGSC_i&xxD!6)d zG{1??({Ma<=Wd4JL%bnEXoBOU_0bbNy3p%mFrMW>#c zzPEvryBevZVUvT^2P&Zobk#9j>vSIW_t?AHy>(^x-Bx~(mvNYb_%$ZFg(s5~oka+Kp(GU68I$h(Vq|fZ zC_u1FM|S)=ldt#5q>&p4r%%p)*7|Rf0}B#-FwHDTo*|P6HB_rz%R;{==hpl#xTt@VLdSrrf~g^ z`IA8ZV1b`UazYpnkn28h&U)$(gdZ*f{n`&kH%Oy54&Z;ebjlh4x?JmnjFAALu}EG} zfGmQ$5vEMJMH`a=+*src#dWK&N1^LFxK9Sa#q_rja$JWra09we<2oL9Q9Sx)?kZFW z$jhOFGE~VcihYlkaZv8?uA7v$*}?2h6i%Qmgc4n~3E(O_`YCRGy~}`NFaj@(?Wz;GS_?T+RqU{S)eD1j$1Gr;C^m z7zDK=xaJ^6``=#Y-2ssNfdRqh0ntJrutGV5Nv&WI%3k1wmD5n+0aRe{0k^!>LFReN zx1g*E>nbyx03KU~UT6->+rG%(owLF=beJxK&a0F;ie1GZ^eKg-VEZb&=s&ajKS#6w zjvC6J#?b|U_(%@uq$c#Q@V_me0S1%)pKz9--{EKwyM}_gOj*Og-NEWLDF_oFtPjG; zXCZ7%#=s}RKr&_5RFN@=H(015AGl4XRN9Bc51`;WWt%vzQvzexDI2BZ@xP~^2$I&7 zA(ndsgLsmA*su8p-~IS q+ZJUZM}`4#Zi@l2F-#HCw*??ha2ta#9s8?H3%YId(*zJG6aF78h1yF1 delta 5107 zcmY*d1zc0@|J{HQlai7V5+f#EN-H%&UP4MFm6QgFfuJK4DG4u#ARsbQL4i>MB1q|w zmWd#pqd~BR-yN@ieE-|$^W1aKIZtf&-p_fyw{(Uwc7_sWYDh^12cY!qXvcPQ!qF;q@b0nYU7 zP&ht}K7j%}P%%|ffm;4F0^i3P0R`a!2wm89L5P3Kfu;tTZJre<{N5}AzsH+E3DS`Q zJLIl`LRMf`JOTBLf(;IV(9(h{(}dXK!cPoSLm(o@fz8vRz}6fOw%3}3VYOsCczLF` za2RTsCWa2sS-uw(6|HLJg)Xf@S8#|+(Z5Y)ER+v+8;btfB3&9sWH6<=U}0)o-jIts zsi?Nko;No&JyZI%@1G&zsG5kKo^Zd7rk_9VIUao9;fC~nv(T0F&Af0&Rp`?x94EIS zUBPyBe5R5#okNiB1Xe--q4|hPyGzhJ?Lurt#Ci09BQ+}rlHpBhm;EmfLw{EbCz)sg zgseAE#f$met1jo;`Z6ihk?O1be3aa$IGV69{nzagziA!M*~E5lMc(Sp+NGm2IUjmn zql((DU9QP~Tn1pt6L`}|$Na-v(P+Zg&?6bAN@2u%KiB*Gmf}Z)R zMENRJgjKMqVbMpzPO{`!J~2Jyu7&xXnTDW?V?IJgy+-35q1)-J8T**?@_-2H`%X+6f5 zIRv`uLp&*?g7L~6+3O*saXT~gWsmhF*FNKw4X$29ePKi02G*)ysenhHv{u9-y?_do ztT(Cu04pk>51n}zu~=wgToY5Cx|MTlNw}GR>+`|6CAhQn=bh@S<7N)`w};;KTywDU z=QWO@RBj$WKOXSgCWg{BD`xl&DS!G}`Mm3$)=%3jzO_C+s+mfTFH5JL>}*(JKs@MqX|o2b#ZBX5P;p7;c)$F1y4HwvJ?KA938$rd)gn_U^CcUtmdaBW57 zlPph>Fz&L`cSScFjcj+7Jif3vxb20Ag~FPstm?9#OrD$e?Y~#1osDB0CFZ9Mu&%iE zSj~wZpFqu6!k%BT)}$F@Z%(d-Pqy07`N8ch2F7z^=S-!r-@j{#&{SM@a8O$P#SySx zZLD_z=I300OCA1YmKV0^lo@>^)THfZvW}s<$^w^#^Ce=kO5ymAnk>H7pK!+NJ-+F7 z1Bb6Y=r)0nZ+hRXUyD+BKAyecZxb+$JTHK5k(nWv*5%2a+u*GDt|rpReYQ}vft zXrIt#!kGO85o^~|9Oc-M5A!S@9Q)O$$&g8u>1=ew?T35h8B{-Z_S78oe=E(-YZhBPe@Y1sUt63A-Cdv>D1nIT~=Rub6$?8g>meFb7Ic@w^%@RN2z72oPZ#Ta%b(P1|&6I z61iO<8hT*)p19Bgd0JgXP{^c{P2~K@^DIXv=dF(u|DFfqD^dMIl8-x)xKIpJRZru@ zDxicyYJG}mh}=1Dfg%B$#H`CiAxPTj^;f4KRMZHUz-_x6)lEq!^mu%72*PI=t$6{Uql#dqm4 zClgaN63!&?v*enz4k1sbaM+yCqUf+i9rw$(YrY%ir1+%cWRB<;r}$8si!6QcNAk~J zk3?dejBaC`>=T<=y=>QVt*4kL>SwYwn$(4ES793qaH)>n(axyV3R5jdXDh#e-N0K- zuUgk|N^|3*D1!Wlz-!M*b}Zc5=;K6I+>1N$&Q%)&8LWUiTYi&aQIj(luA< zN5R<8Y8L#*i0xBio$jWcaiZ4S2w3#R@CGemesy~akKP)2GojQF6!$}!_RdUJPBevX zG#~uz%Yirb0@1wgQ;ayb=qD}6{=QXxjuZQ@@kxbN!QWhtEvuhS2yAZe8fZy6*4Inr zdSyR9Dec4HrE|I=z-U;IlH;_h#7e^Hq}gaJ<-z^}{*s!m^66wu2=(*EM0UaV*&u1q zJrq!K23TO8a(ecSQFdD$y+`xu)Xk36Z*;1i{hS=H2E<8<5yHuHG~22-S+Jq|3HMAw z%qBz3auT=M!=5F|Wqke|I^E8pmJ-}>_DwX5w%d3MSdC>xW%$ocm8w8HRdZ|^#cEt1 zM*I7S6sLQq;;Mecet(Q()+?s+&MeVLOvx}(MkvytkvLHl7h*N0AT1#AqC&(he(^%przH`KqA$z_dAvJJb409@F)fYwD$JW_{_Oie8!@VdJE zU>D$@B?LawAf5$;`AZ1E!krn=aAC%4+YQrzL!59yl1;|T2)u=RBYA8lk0Ek&gS!Rb zt0&hVuyhSa0}rpZGjTA>Gz}>Uv*4)F zf7S%D2nfA7x?gPEXZWk8DZimQs#xi0?So_k`2zb!UVQEAcbvjPLK9v>J~!awnxGpq zEh$EPOc4q&jywmglnC&D)1-P0DH!@)x;uJwMHdhPh>ZLWDw+p1pf52{X2dk{_|UOmakJa4MHu?CY`6Hhv!!d7=aNwiB5z zb*Wlq1zf^3iDlPf)b_SzI*{JCx2jN;*s~ra8NeB!PghqP!0po-ZL?0Jk;2~*~sCQ<%wU`mRImd)~!23RS?XJu|{u( ztFPy3*F=ZhJmBugTv48WX)4U*pNmm~4oD4}$*-92&<)n=R)5lT z-VpbEDk>(C1hoo#-H_u0`#%L6L$ zln(}h2*Cl(5(JtVM{YZ26@Fwmp;?Qt}9$_F%`?+-JHbC;bPZj8PLq9 zWo-KFw!i&r8WuA-!3F_m9!24Z(RhalAUR~_H#Ln=$%b5GY z)oB)zO%J5TY}&BXq^7#M>euVL%01Tzj4$6^ZOjT*7@zr~q@6GEjGi)nbwzSL`TiLN z{DVG~I$w@%^#tD{>1Ap@%=XogG_^Hvy_xiRn4yy?LKsC+ zU!S79X8orh&D%>1S`x2iyi&(iG&r#YT{}~iy(FIOo8?MZU#eo*c*(RjAGj@uDi zARJur)-*{n0PgW~&mFeg`MJ?(Kr;NUom)jh?ozZtyywN9bea6ikQlh}953Oul~N%4 z@Sx!@>?l1e7V*@HZMJx!gMo0TeXdU~#W6^n?YVQJ$)nuFRkvKbfwv_s*2g(!wPO|@ zvuXF=2MiPIX)A7x!|BthSa$GB%ECnuZe_Scx&AlnC z!~6C_SF24#@^VMIw)a-7{00}}Cr5NImPbW8OTIHoo6@NcxLVTna8<<;uy~YaaeMnd z;k_ynYc_8jQn9vW_W8QLkgaHtmwGC}wRcgZ^I^GPbz{lW)p#YYoinez1MjkY%6LBd z+Vr>j&^!?b-*Vk>8I!28o`r3w&^Lal8@=50zV4&9V9oXI{^r8;JmVeos&wf?O!;_o zk))^k*1fvYw9?WrS!sG2TcX`hH@Y3mF&@{i05;_AV{>Umi8{uZP_0W5_1V2yHU<)E z+qviK*7SJtnL;76{WK!?Pv$-!w$08<%8Qy|sB|P%GiV1<+dHw*sj!C~SjsB6+1L@so+Q~n# z+Uc5+Uz+mGmkR@>H7D*c?mm8WQz;3VOpktU_DeBi>3#@z zmLe;3gP<7KPy>~k47nEeT?G?7e2g6316Xdb_y+ja5C9Ayg6QTNr~&Kbs(1>7zp|f@le;9B z1e(+Ga%jPWR7oc}=XcB4$z?YD)l;%#U;}~gZzGViI=fwu9OAPCCK!0w>Ay^#$b49k zT&|M?JaIyRT<;@*t_jp1ifWPvL;{maf6o0T#X!#9YX;0Q;LTQ0}0tg^_Ru4pkSr4#P zmnW|D0`A#Ie6pEfBDv39=jN2;kiUoT6I&kChsbI!jMuY6zuZql5!&i%5!c zjsHlXtjT;NV?jAb`%vy)JOK_j1rponLqc>(2qgYlLPEs>|0QV<=Pw~C`fLFKJJitt zyC6003{rxCsmtGKjhB%W2W~*%vKH8l$pZoOFT*K@uL9%CD^3rh=ZtuTU1 zJpf4|%n^yjh#dKSSCJI8;YU*CD!8Wv20*e5`-fya^75@ADLU^RdHDg3Bk3k6)dGi7 z!!z;|O1h$8q!vO*w6 I6Xdi10eY*&F8}}l diff --git a/kotlin/gradle/wrapper/gradle-wrapper.properties b/kotlin/gradle/wrapper/gradle-wrapper.properties index 2b22d057a07..6ec1567a0f8 100644 --- a/kotlin/gradle/wrapper/gradle-wrapper.properties +++ b/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/kotlin/gradlew b/kotlin/gradlew index 65dcd68d65c..79a61d421cc 100755 --- a/kotlin/gradlew +++ b/kotlin/gradlew @@ -144,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts index 780048b49fa..e82797fed7a 100644 --- a/kotlin/wallet-core-kotlin/build.gradle.kts +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -38,8 +38,6 @@ kotlin { } val androidMain by getting { - kotlin.srcDir(projectDir.resolve("../../jni/cpp")) - kotlin.srcDir(projectDir.resolve("../../jni/kotlin")) kotlin.srcDir(projectDir.resolve("src/androidMain/generated")) } val commonMain by getting { diff --git a/kotlin/wallet-core-kotlin/src/androidMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/androidMain/kotlin/com/trustwallet/core/AnySigner.kt index 4daa57c663b..374d66218b9 100644 --- a/kotlin/wallet-core-kotlin/src/androidMain/kotlin/com/trustwallet/core/AnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/androidMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -6,10 +6,17 @@ package com.trustwallet.core -actual external fun signImpl(input: ByteArray, coin: CoinType): ByteArray +actual object AnySigner { -actual external fun supportsJsonImpl(coin: CoinType): Boolean + @JvmStatic + actual external fun sign(input: ByteArray, coin: CoinType): ByteArray -actual external fun signJsonImpl(json: String, key: ByteArray, coin: CoinType): String + @JvmStatic + actual external fun supportsJson(coin: CoinType): Boolean -actual external fun planImpl(input: ByteArray, coin: CoinType): ByteArray + @JvmStatic + actual external fun signJson(json: String, key: ByteArray, coin: CoinType): String + + @JvmStatic + actual external fun plan(input: ByteArray, coin: CoinType): ByteArray +} diff --git a/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt index df9455e9943..85e195c229e 100644 --- a/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -9,24 +9,19 @@ package com.trustwallet.core import com.squareup.wire.Message import com.squareup.wire.ProtoAdapter -object AnySigner { +expect object AnySigner { - fun > sign(input: Message<*, *>, coin: CoinType, adapter: ProtoAdapter): T = - adapter.decode(signImpl(input.encode(), coin)) + fun sign(input: ByteArray, coin: CoinType): ByteArray - fun supportsJson(coin: CoinType): Boolean = supportsJsonImpl(coin) + fun supportsJson(coin: CoinType): Boolean - fun signJson(json: String, key: ByteArray, coin: CoinType): String = - signJsonImpl(json, key, coin) + fun signJson(json: String, key: ByteArray, coin: CoinType): String - fun > plan(input: Message<*, *>, coin: CoinType, adapter: ProtoAdapter): T = - adapter.decode(planImpl(input.encode(), coin)) + fun plan(input: ByteArray, coin: CoinType): ByteArray } -internal expect fun signImpl(input: ByteArray, coin: CoinType): ByteArray +fun > AnySigner.sign(input: Message<*, *>, coin: CoinType, adapter: ProtoAdapter): T = + adapter.decode(sign(input.encode(), coin)) -internal expect fun supportsJsonImpl(coin: CoinType): Boolean - -internal expect fun signJsonImpl(json: String, key: ByteArray, coin: CoinType): String - -internal expect fun planImpl(input: ByteArray, coin: CoinType): ByteArray +fun > AnySigner.plan(input: Message<*, *>, coin: CoinType, adapter: ProtoAdapter): T = + adapter.decode(plan(input.encode(), coin)) diff --git a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt index ad6aa3e3888..fd1cb8ed651 100644 --- a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -6,14 +6,17 @@ package com.trustwallet.core -actual fun signImpl(input: ByteArray, coin: CoinType): ByteArray = - TWAnySignerSign(input.toTwData(), coin.value)!!.readTwBytes()!! +actual object AnySigner { -actual fun supportsJsonImpl(coin: CoinType): Boolean = - TWAnySignerSupportsJSON(coin.value) + actual fun sign(input: ByteArray, coin: CoinType): ByteArray = + TWAnySignerSign(input.toTwData(), coin.value)!!.readTwBytes()!! -actual fun signJsonImpl(json: String, key: ByteArray, coin: CoinType): String = - TWAnySignerSignJSON(json.toTwString(), key.toTwData(), coin.value).fromTwString()!! + actual fun supportsJson(coin: CoinType): Boolean = + TWAnySignerSupportsJSON(coin.value) -actual fun planImpl(input: ByteArray, coin: CoinType): ByteArray = - TWAnySignerPlan(input.toTwData(), coin.value)?.readTwBytes()!! + actual fun signJson(json: String, key: ByteArray, coin: CoinType): String = + TWAnySignerSignJSON(json.toTwString(), key.toTwData(), coin.value).fromTwString()!! + + actual fun plan(input: ByteArray, coin: CoinType): ByteArray = + TWAnySignerPlan(input.toTwData(), coin.value)?.readTwBytes()!! +} diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt index 61bdabba2bb..e71a5458a55 100644 --- a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt @@ -4,24 +4,29 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +import com.trustwallet.core.* import kotlin.js.Promise @JsExport @JsName("WalletCoreKotlin") object WalletCore { - internal var Instance: dynamic = null + internal lateinit var Instance: JsWalletCore - fun init(): Promise = - WalletCoreExports.initWasm() - .then { walletCore: dynamic -> - Instance = walletCore - walletCore - } + fun init(): Promise = + if (::Instance.isInitialized) { + Promise.resolve(Instance) + } else { + WalletCoreExports.initWasm() + .then { walletCore -> + Instance = walletCore + walletCore + } + } } @JsModule("@trustwallet/wallet-core") @JsNonModule -internal external object WalletCoreExports { - fun initWasm(): Promise +private external object WalletCoreExports { + fun initWasm(): Promise } diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt index 7f4260201c7..b9e28700f97 100644 --- a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -8,18 +8,17 @@ package com.trustwallet.core import WalletCore -internal actual fun signImpl(input: ByteArray, coin: CoinType): ByteArray = - WalletCore.Instance.AnySigner.sign(input.toUInt8Array(), coin._value) - .unsafeCast() - .toByteArray() +actual object AnySigner { -internal actual fun supportsJsonImpl(coin: CoinType): Boolean = - WalletCore.Instance.AnySigner.supportsJSON(coin._value) as Boolean + actual fun sign(input: ByteArray, coin: CoinType): ByteArray = + WalletCore.Instance.AnySigner.sign(input.asUInt8Array(), coin.jsValue).asByteArray() -internal actual fun signJsonImpl(json: String, key: ByteArray, coin: CoinType): String = - TODO() + actual fun supportsJson(coin: CoinType): Boolean = + WalletCore.Instance.AnySigner.supportsJSON(coin.jsValue) -internal actual fun planImpl(input: ByteArray, coin: CoinType): ByteArray = - WalletCore.Instance.AnySigner.plan(input.toUInt8Array(), coin._value) - .unsafeCast() - .toByteArray() + actual fun signJson(json: String, key: ByteArray, coin: CoinType): String = + TODO() + + actual fun plan(input: ByteArray, coin: CoinType): ByteArray = + WalletCore.Instance.AnySigner.plan(input.asUInt8Array(), coin.jsValue).asByteArray() +} diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt new file mode 100644 index 00000000000..615d8c38db9 --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt @@ -0,0 +1,24 @@ +// Copyright © 2017-2023 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 + +import org.khronos.webgl.Int8Array +import org.khronos.webgl.Uint8Array + +internal typealias UInt8Array = Uint8Array + +fun Int8Array.asByteArray(): ByteArray = + unsafeCast() + +fun Uint8Array.asByteArray(): ByteArray = + Int8Array(buffer, byteOffset, length).asByteArray() + +fun ByteArray.asInt8Array(): Int8Array = + unsafeCast() + +fun ByteArray.asUInt8Array(): Uint8Array = + asInt8Array().let { Uint8Array(it.buffer, it.byteOffset, it.length) } diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt new file mode 100644 index 00000000000..f387eccbe7d --- /dev/null +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt @@ -0,0 +1,20 @@ +// Copyright © 2017-2023 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 + +@JsModule("@trustwallet/wallet-core") +@JsName("AnySigner") +external class JsAnySigner { + companion object { + fun sign(data: UInt8Array, coin: JsCoinType): UInt8Array + fun plan(data: UInt8Array, coin: JsCoinType): UInt8Array + fun supportsJSON(coin: JsCoinType): Boolean + } +} + +inline val JsWalletCore.AnySigner: JsAnySigner.Companion + get() = asDynamic().AnySigner.unsafeCast() diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/UInt8Array.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsWalletCore.kt similarity index 53% rename from kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/UInt8Array.kt rename to kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsWalletCore.kt index 001d22c0912..f2d75c7c361 100644 --- a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/UInt8Array.kt +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsWalletCore.kt @@ -4,14 +4,10 @@ // 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 - -import org.khronos.webgl.get +@file:Suppress("PropertyName") -internal typealias UInt8Array = org.khronos.webgl.Uint8Array - -internal fun UInt8Array.toByteArray(): ByteArray = - ByteArray(length, ::get) +package com.trustwallet.core -internal fun ByteArray.toUInt8Array(): UInt8Array = - UInt8Array(toTypedArray()) +@JsModule("@trustwallet/wallet-core") +@JsName("WalletCore") +external interface JsWalletCore diff --git a/src/interface/TWAccount.cpp b/src/interface/TWAccount.cpp index 73a624738c7..1759273d9a3 100644 --- a/src/interface/TWAccount.cpp +++ b/src/interface/TWAccount.cpp @@ -10,7 +10,8 @@ using namespace TW; -struct TWAccount* _Nonnull TWAccountCreate(TWString* _Nonnull address, enum TWCoinType coin, +struct TWAccount* _Nonnull TWAccountCreate(TWString* _Nonnull address, + enum TWCoinType coin, enum TWDerivation derivation, TWString* _Nonnull derivationPath, TWString* _Nonnull publicKey, @@ -33,6 +34,10 @@ TWString* _Nonnull TWAccountAddress(struct TWAccount* _Nonnull account) { return TWStringCreateWithUTF8Bytes(account->impl.address.c_str()); } +enum TWCoinType TWAccountCoin(struct TWAccount* _Nonnull account) { + return account->impl.coin; +} + enum TWDerivation TWAccountDerivation(struct TWAccount* _Nonnull account) { return account->impl.derivation; } @@ -48,7 +53,3 @@ TWString* _Nonnull TWAccountPublicKey(struct TWAccount* _Nonnull account) { TWString* _Nonnull TWAccountExtendedPublicKey(struct TWAccount* _Nonnull account) { return TWStringCreateWithUTF8Bytes(account->impl.extendedPublicKey.c_str()); } - -enum TWCoinType TWAccountCoin(struct TWAccount* _Nonnull account) { - return account->impl.coin; -} From c33d64d1562dc00fd4c0aed69ab60d8114dd9a4a Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 2 Mar 2023 15:26:55 +0100 Subject: [PATCH 111/426] [Tezos]: Message Signer (#2962) --- .../tezos/TestTezosMessageSigner.kt | 42 +++++++++++++ .../TrustWalletCore/TWTezosMessageSigner.h | 53 ++++++++++++++++ src/Tezos/MessageSigner.cpp | 55 +++++++++++++++++ src/Tezos/MessageSigner.h | 40 ++++++++++++ src/interface/TWTezosMessageSigner.cpp | 31 ++++++++++ swift/Tests/Blockchains/TezosTests.swift | 21 +++++++ tests/chains/Tezos/MessageSignerTests.cpp | 61 +++++++++++++++++++ 7 files changed, 303 insertions(+) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/tezos/TestTezosMessageSigner.kt create mode 100644 include/TrustWalletCore/TWTezosMessageSigner.h create mode 100644 src/Tezos/MessageSigner.cpp create mode 100644 src/Tezos/MessageSigner.h create mode 100644 src/interface/TWTezosMessageSigner.cpp create mode 100644 tests/chains/Tezos/MessageSignerTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/tezos/TestTezosMessageSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/tezos/TestTezosMessageSigner.kt new file mode 100644 index 00000000000..57b06f83714 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/tezos/TestTezosMessageSigner.kt @@ -0,0 +1,42 @@ +package com.trustwallet.core.app.blockchains.tezos + +import com.trustwallet.core.app.utils.Numeric +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import wallet.core.jni.CoinType +import wallet.core.jni.TezosMessageSigner +import wallet.core.jni.PrivateKey +import java.util.regex.Pattern + +class TestTezosMessageSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testMessageSignerSignAndVerify() { + val data = Numeric.hexStringToByteArray("91b4fb8d7348db2e7de2693f58ce1cceb966fa960739adac1d9dba2cbaa0940a") + val privateKey = PrivateKey(data) + val msg = "05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64" + val signature = TezosMessageSigner.signMessage(privateKey, msg) + assertEquals("edsigu3se2fcEJUCm1aqxjzbHdf7Wsugr4mLaA9YM2UVZ9Yy5meGv87VqHN3mmDeRwApTj1JKDaYjqmLZifSFdWCqBoghqaowwJ", signature) + val pubKey = privateKey.getPublicKey(CoinType.TEZOS) + assertTrue(TezosMessageSigner.verifyMessage(pubKey, msg, signature)) + } + + @Test + fun testMessageSignerInputToPayload() { + val payload = TezosMessageSigner.inputToPayload("Tezos Signed Message: testUrl 2023-02-08T10:36:18.454Z Hello World") + val expected = "05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64" + assertEquals(expected, payload) + } + + @Test + fun testMessageSignerFormatMessage() { + val formatedMsg = TezosMessageSigner.formatMessage("Hello World", "testUrl") + val regex = Pattern.compile("Tezos Signed Message: \\S+ \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z .+") + assertTrue(regex.matcher(formatedMsg).matches()) + } +} diff --git a/include/TrustWalletCore/TWTezosMessageSigner.h b/include/TrustWalletCore/TWTezosMessageSigner.h new file mode 100644 index 00000000000..bcb99bf8a85 --- /dev/null +++ b/include/TrustWalletCore/TWTezosMessageSigner.h @@ -0,0 +1,53 @@ +// Copyright © 2017-2023 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" +#include "TWPrivateKey.h" +#include "TWPublicKey.h" + +TW_EXTERN_C_BEGIN + +/// Tezos message signing, verification and utilities. +TW_EXPORT_STRUCT +struct TWTezosMessageSigner; + +/// Implement format input as described in https://tezostaquito.io/docs/signing/ +/// +/// \param message message to format e.g: Hello, World +/// \param dAppUrl the app url, e.g: testUrl +/// \returns the formatted message as a string +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWTezosMessageSignerFormatMessage(TWString* _Nonnull message, TWString* _Nonnull url); + +/// Implement input to payload as described in: https://tezostaquito.io/docs/signing/ +/// +/// \param message formatted message to be turned into an hex payload +/// \return the hexpayload of the formated message as a hex string +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWTezosMessageSignerInputToPayload(TWString* _Nonnull message); + +/// Sign a message as described in https://tezostaquito.io/docs/signing/ +/// +/// \param privateKey: the private key used for signing +/// \param message: A custom message payload (hex) which is input to the signing. +/// \returns the signature, Hex-encoded. On invalid input empty string is returned. Returned object needs to be deleted after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWTezosMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message); + +/// Verify signature for a message as described in https://tezostaquito.io/docs/signing/ +/// +/// \param pubKey: pubKey that will verify the message from the signature +/// \param message: the message signed as a payload (hex) +/// \param signature: in Base58-encoded form. +/// \returns false on any invalid input (does not throw), true if the message can be verified from the signature +TW_EXPORT_STATIC_METHOD +bool TWTezosMessageSignerVerifyMessage(const struct TWPublicKey* _Nonnull pubKey, TWString* _Nonnull message, TWString* _Nonnull signature); + +TW_EXTERN_C_END diff --git a/src/Tezos/MessageSigner.cpp b/src/Tezos/MessageSigner.cpp new file mode 100644 index 00000000000..34e9432b132 --- /dev/null +++ b/src/Tezos/MessageSigner.cpp @@ -0,0 +1,55 @@ +// Copyright © 2017-2023 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 +#include +#include +#include +#include + +#include "Base58.h" +#include "HexCoding.h" +#include "Tezos/MessageSigner.h" + +namespace TW::Tezos { + +static const Data gEdSigPrefix{9, 245, 205, 134, 18}; +static const std::string gMsgPrefix{"Tezos Signed Message:"}; + +std::string MessageSigner::inputToPayload(const std::string& input) { + using namespace std::string_literals; + auto bytes = data(input); + size_t bytesLength = bytes.size(); + std::string addPadding = std::string(8 - std::to_string(bytesLength).size(), '0') + hex(uint64_t(bytesLength)); + std::string paddedBytesLength = addPadding.substr(addPadding.size() - 8); + std::string payloadBytes = "05"s + "01"s + paddedBytesLength + hex(bytes); + return payloadBytes; +} + +std::string MessageSigner::formatMessage(const std::string& message, const std::string& dAppUrl) { + auto now = std::chrono::system_clock::now(); + auto now_time = std::chrono::system_clock::to_time_t(now); + auto now_ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; + std::ostringstream oss; + oss << gMsgPrefix << " " << dAppUrl << " "; + oss << std::put_time(std::gmtime(&now_time), "%FT%T.") << std::setw(3) << std::setfill('0') << now_ms.count() << "Z"; + oss << " " << message; + return oss.str(); +} + +std::string MessageSigner::signMessage(const PrivateKey& privateKey, const std::string& message) { + auto signature = privateKey.sign(Hash::blake2b(parse_hex(message), 32), TWCurveED25519); + return Base58::encodeCheck(concat(gEdSigPrefix, signature)); +} + +bool MessageSigner::verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept { + auto decoded = Base58::decodeCheck(signature); + auto rawSignature = subData(decoded, gEdSigPrefix.size()); + auto msg = Hash::blake2b(parse_hex(message), 32); + return publicKey.verify(rawSignature, msg); +} + +} // namespace TW::Tezos diff --git a/src/Tezos/MessageSigner.h b/src/Tezos/MessageSigner.h new file mode 100644 index 00000000000..7ef7d6b3ed5 --- /dev/null +++ b/src/Tezos/MessageSigner.h @@ -0,0 +1,40 @@ +// Copyright © 2017-2023 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 "PrivateKey.h" +#include "Data.h" + +namespace TW::Tezos { + class MessageSigner { + public: + /// implement format input as described in https://tezostaquito.io/docs/signing/ + /// \param message message to format e.g: Hello, World + /// \param dAppUrl the app url, e.g: testUrl + /// \return the formatted message as a string + static std::string formatMessage(const std::string& message, const std::string& dAppUrl); + + /// implement input to payload as described in: https://tezostaquito.io/docs/signing/ + /// + /// \param input formatted input to be turned into an hex payload + /// \return the hexpayload of the formated input as a hex string + static std::string inputToPayload(const std::string& input); + + /// implement signing as described in https://tezostaquito.io/docs/signing/ + /// \param privateKey the private key to sign with + /// \param message message to sign + /// \return base58 signed message + static std::string signMessage(const PrivateKey& privateKey, const std::string& message); + + /// implement verification as described in https://tezostaquito.io/docs/signing/ + /// \param publicKey publickey to verify the signed message + /// \param message message to be verified as a string + /// \param signature signature to verify the message against + /// \return true if the message match the signature, false otherwise + static bool verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept;; + }; +} diff --git a/src/interface/TWTezosMessageSigner.cpp b/src/interface/TWTezosMessageSigner.cpp new file mode 100644 index 00000000000..91b3352ec7f --- /dev/null +++ b/src/interface/TWTezosMessageSigner.cpp @@ -0,0 +1,31 @@ +// Copyright © 2017-2023 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 +#include "Tezos/MessageSigner.h" + +bool TWTezosMessageSignerVerifyMessage(const struct TWPublicKey* _Nonnull publicKey, TWString* _Nonnull message, TWString* _Nonnull signature) { + return TW::Tezos::MessageSigner::verifyMessage(publicKey->impl, TWStringUTF8Bytes(message), TWStringUTF8Bytes(signature)); +} + +TWString* _Nonnull TWTezosMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull message) { + try { + const auto signature = TW::Tezos::MessageSigner::signMessage(privateKey->impl, TWStringUTF8Bytes(message)); + return TWStringCreateWithUTF8Bytes(signature.c_str()); + } catch (...) { + return TWStringCreateWithUTF8Bytes(""); + } +} + +TWString* TWTezosMessageSignerFormatMessage(TWString* _Nonnull message, TWString* _Nonnull url) { + const auto formatedMessage = TW::Tezos::MessageSigner::formatMessage(TWStringUTF8Bytes(message), TWStringUTF8Bytes(url)); + return TWStringCreateWithUTF8Bytes(formatedMessage.c_str()); +} + +TWString* TWTezosMessageSignerInputToPayload(TWString* message) { + const auto payload = TW::Tezos::MessageSigner::inputToPayload(TWStringUTF8Bytes(message)); + return TWStringCreateWithUTF8Bytes(payload.c_str()); +} diff --git a/swift/Tests/Blockchains/TezosTests.swift b/swift/Tests/Blockchains/TezosTests.swift index a6076a3beaa..df36e377712 100644 --- a/swift/Tests/Blockchains/TezosTests.swift +++ b/swift/Tests/Blockchains/TezosTests.swift @@ -113,6 +113,27 @@ class TezosTests: XCTestCase { XCTAssertEqual(output.encoded.hexString, expected) } + + public func testMessageSignerSignAndVerify() { + let privateKey = PrivateKey(data: Data(hexString: "91b4fb8d7348db2e7de2693f58ce1cceb966fa960739adac1d9dba2cbaa0940a")!)! + let msg = "05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64" + let signature = TezosMessageSigner.signMessage(privateKey: privateKey, message: msg) + XCTAssertEqual(signature, "edsigu3se2fcEJUCm1aqxjzbHdf7Wsugr4mLaA9YM2UVZ9Yy5meGv87VqHN3mmDeRwApTj1JKDaYjqmLZifSFdWCqBoghqaowwJ") + let pubKey = privateKey.getPublicKey(coinType: .tezos) + XCTAssertTrue(TezosMessageSigner.verifyMessage(pubKey: pubKey, message: msg, signature: signature)) + } + + public func testMessageSignerInputToPayload() { + let payload = TezosMessageSigner.inputToPayload(message: "Tezos Signed Message: testUrl 2023-02-08T10:36:18.454Z Hello World"); + let expected = "05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64"; + XCTAssertEqual(payload, expected); + } + + public func testMessageSignerFormatMessage() { + let formatedMsg = TezosMessageSigner.formatMessage(message: "Hello World", url: "testUrl") + let regex = try! NSRegularExpression(pattern: "Tezos Signed Message: \\S+ \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z .+") + XCTAssertTrue(regex.firstMatch(in: formatedMsg, range: NSRange(location: 0, length: formatedMsg.utf16.count)) != nil) + } public func testSigning() { let privateKeyData = Data(hexString: "c6377a4cc490dc913fc3f0d9cf67d293a32df4547c46cb7e9e33c3b7b97c64d8")! diff --git a/tests/chains/Tezos/MessageSignerTests.cpp b/tests/chains/Tezos/MessageSignerTests.cpp new file mode 100644 index 00000000000..4fc6faa70c8 --- /dev/null +++ b/tests/chains/Tezos/MessageSignerTests.cpp @@ -0,0 +1,61 @@ +// Copyright © 2017-2023 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 +#include + +#include "HexCoding.h" +#include "TestUtilities.h" +#include +#include + +namespace TW::Tezos::tests { +TEST(TezosMessageSigner, inputToPayload) { + auto payload = Tezos::MessageSigner::inputToPayload("Tezos Signed Message: testUrl 2023-02-08T10:36:18.454Z Hello World"); + ASSERT_EQ(payload, "05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64"); +} + +TEST(TezosMessageSigner, formatMessage) { + auto formatMessage = Tezos::MessageSigner::formatMessage("Hello World", "testUrl"); + std::regex regex("Tezos Signed Message: \\S+ \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z .+"); + ASSERT_TRUE(std::regex_match(formatMessage, regex)); +} + +TEST(TezosMessageSigner, SignMessage) { + auto payload = Tezos::MessageSigner::inputToPayload("Tezos Signed Message: testUrl 2023-02-08T10:36:18.454Z Hello World"); + PrivateKey privKey(parse_hex("91b4fb8d7348db2e7de2693f58ce1cceb966fa960739adac1d9dba2cbaa0940a")); + auto result = Tezos::MessageSigner::signMessage(privKey, payload); + auto expected = "edsigu3se2fcEJUCm1aqxjzbHdf7Wsugr4mLaA9YM2UVZ9Yy5meGv87VqHN3mmDeRwApTj1JKDaYjqmLZifSFdWCqBoghqaowwJ"; + ASSERT_EQ(result, expected); + ASSERT_TRUE(Tezos::MessageSigner::verifyMessage(privKey.getPublicKey(TWPublicKeyTypeED25519), payload, result)); +} + +TEST(TWTezosMessageSigner, formatMessage) { + const auto message = STRING("Hello World"); + const auto dappUrl = STRING("testUrl"); + auto formattedMsg = WRAPS(TWTezosMessageSignerFormatMessage(message.get(), dappUrl.get())); + std::regex regex("Tezos Signed Message: \\S+ \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z .+"); + ASSERT_TRUE(std::regex_match(std::string(TWStringUTF8Bytes(formattedMsg.get())), regex)); +} + +TEST(TWTezosMessageSigner, inputToPayload) { + const auto message = STRING("Tezos Signed Message: testUrl 2023-02-08T10:36:18.454Z Hello World"); + const auto expected = "05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64"; + auto payload = WRAPS(TWTezosMessageSignerInputToPayload(message.get())); + ASSERT_EQ(std::string(TWStringUTF8Bytes(payload.get())), expected); +} + +TEST(TWTezosMessageSigner, SignAndVerify) { + const auto privKeyData = "91b4fb8d7348db2e7de2693f58ce1cceb966fa960739adac1d9dba2cbaa0940a"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto message = STRING("05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64"); + + const auto pubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKey(privateKey.get(), TWCoinTypeTezos)); + const auto signature = WRAPS(TWTezosMessageSignerSignMessage(privateKey.get(), message.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "edsigu3se2fcEJUCm1aqxjzbHdf7Wsugr4mLaA9YM2UVZ9Yy5meGv87VqHN3mmDeRwApTj1JKDaYjqmLZifSFdWCqBoghqaowwJ"); + EXPECT_TRUE(TWTezosMessageSignerVerifyMessage(pubKey.get(), message.get(), signature.get())); +} +} // namespace TW::Tezos::tests From 6564c8a31746bea8c54da03d4a490cb6886fcc09 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 2 Mar 2023 15:27:10 +0100 Subject: [PATCH 112/426] [Solana]: Refactor, Optimizations (#2949) --- src/Solana/AccountMeta.h | 2 +- src/Solana/Address.cpp | 5 ++-- src/Solana/CompiledInstruction.h | 5 ++-- src/Solana/Entry.cpp | 4 +-- src/Solana/Entry.h | 4 +-- src/Solana/Program.cpp | 2 +- src/Solana/Program.h | 2 +- src/Solana/Signature.cpp | 13 --------- src/Solana/Signature.h | 30 --------------------- src/Solana/Signer.cpp | 17 +++++------- src/Solana/Transaction.cpp | 7 +++-- src/Solana/Transaction.h | 9 +++---- src/Solana/VersionedTransaction.cpp | 7 +++-- src/Solana/VersionedTransaction.h | 7 +++-- tests/chains/Solana/SignerTests.cpp | 34 ++++++++++++------------ tests/chains/Solana/TransactionTests.cpp | 15 +++++------ 16 files changed, 55 insertions(+), 108 deletions(-) delete mode 100644 src/Solana/Signature.cpp delete mode 100644 src/Solana/Signature.h diff --git a/src/Solana/AccountMeta.h b/src/Solana/AccountMeta.h index c5545603fde..a8f935a907a 100644 --- a/src/Solana/AccountMeta.h +++ b/src/Solana/AccountMeta.h @@ -14,7 +14,7 @@ struct AccountMeta { Address account; bool isSigner; bool isReadOnly; - AccountMeta(const Address& address, bool isSigner, bool isReadOnly): account(address), isSigner(isSigner), isReadOnly(isReadOnly) {} + AccountMeta(const Address& address, bool isSigner, bool isReadOnly) noexcept : account(address), isSigner(isSigner), isReadOnly(isReadOnly) {} }; } diff --git a/src/Solana/Address.cpp b/src/Solana/Address.cpp index b1df361aaab..25b6699e41c 100644 --- a/src/Solana/Address.cpp +++ b/src/Solana/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -52,8 +52,7 @@ std::string Address::string() const { } Data Address::vector() const { - Data vec(std::begin(bytes), std::end(bytes)); - return vec; + return Data(begin(bytes), end(bytes)); } Address Address::defaultTokenAddress(const Address& tokenMintAddress) { diff --git a/src/Solana/CompiledInstruction.h b/src/Solana/CompiledInstruction.h index 1507886f185..913791ec3fc 100644 --- a/src/Solana/CompiledInstruction.h +++ b/src/Solana/CompiledInstruction.h @@ -26,8 +26,9 @@ struct CompiledInstruction { /// Supplied address vector is expected to contain all addresses and programId from the instruction; they are replaced by index into the address vector. CompiledInstruction(const Instruction& instruction, const std::vector
& addresses): addresses(addresses) { programIdIndex = findAccount(instruction.programId); - for (auto& account: instruction.accounts) { - accounts.push_back(findAccount(account.account)); + accounts.reserve(instruction.accounts.size()); + for (auto&& account: instruction.accounts) { + accounts.emplace_back(findAccount(account.account)); } data = instruction.data; } diff --git a/src/Solana/Entry.cpp b/src/Solana/Entry.cpp index 9ce90b67609..09df76df7f3 100644 --- a/src/Solana/Entry.cpp +++ b/src/Solana/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -14,8 +14,6 @@ using namespace std; namespace TW::Solana { -// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. - bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { return Address::isValid(address); } diff --git a/src/Solana/Entry.h b/src/Solana/Entry.h index 3812f579ad9..81d7ed64446 100644 --- a/src/Solana/Entry.h +++ b/src/Solana/Entry.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,7 +6,7 @@ #pragma once -#include "../CoinEntry.h" +#include "CoinEntry.h" namespace TW::Solana { diff --git a/src/Solana/Program.cpp b/src/Solana/Program.cpp index f9f196bb209..1605aebba97 100644 --- a/src/Solana/Program.cpp +++ b/src/Solana/Program.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2022 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/src/Solana/Program.h b/src/Solana/Program.h index e2880c3c173..c91be4cb80c 100644 --- a/src/Solana/Program.h +++ b/src/Solana/Program.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/src/Solana/Signature.cpp b/src/Solana/Signature.cpp deleted file mode 100644 index e054ac0f8c2..00000000000 --- a/src/Solana/Signature.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright © 2017-2023 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 "Solana/Signature.h" - -namespace TW::Solana { - bool Signature::operator==(const Signature& v) const { - return bytes == v.bytes; - } -} diff --git a/src/Solana/Signature.h b/src/Solana/Signature.h deleted file mode 100644 index 3a1983cbaa9..00000000000 --- a/src/Solana/Signature.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2023 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 -#include - -#include "Base58.h" - -namespace TW::Solana { -class Signature { -public: - static const size_t size = 64; - /// Signature data - std::array bytes; - - Signature(const std::string& string) { - const auto data = Base58::decode(string); - std::copy(data.begin(), data.end(), this->bytes.begin()); - } - Signature(const std::array& bytes) { this->bytes = bytes; } - Signature(const Data& bytes) { std::copy(bytes.begin(), bytes.end(), this->bytes.begin()); } - - bool operator==(const Signature& v) const; -}; -} diff --git a/src/Solana/Signer.cpp b/src/Solana/Signer.cpp index bab36a4cd76..a2359b6006c 100644 --- a/src/Solana/Signer.cpp +++ b/src/Solana/Signer.cpp @@ -21,8 +21,7 @@ void Signer::sign(const std::vector& privateKeys, VersionedTransacti auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); auto index = transaction.getAccountIndex(address); auto message = transaction.messageData(); - auto signature = Signature(privateKey.sign(message, TWCurveED25519)); - transaction.signatures[index] = signature; + transaction.signatures[index] = privateKey.sign(message, TWCurveED25519); } } @@ -194,16 +193,14 @@ void Signer::signUpdateBlockhash(const std::vector& privateKeys, // This method does not confirm that PrivateKey order matches that encoded in the messageData // That order must be correct for the Transaction to succeed on Solana Data Signer::signRawMessage(const std::vector& privateKeys, const Data messageData) { - std::vector signatures; - for (auto privateKey : privateKeys) { - auto signature = Signature(privateKey.sign(messageData, TWCurveED25519)); - signatures.push_back(signature); + std::vector signatures; + for (auto &&privateKey : privateKeys) { + signatures.emplace_back(privateKey.sign(messageData, TWCurveED25519)); } Data buffer; - append(buffer, shortVecLength(signatures)); - for (auto signature : signatures) { - Data signature_vec(signature.bytes.begin(), signature.bytes.end()); - append(buffer, signature_vec); + append(buffer, shortVecLength(signatures)); + for (auto &&signature : signatures) { + append(buffer, signature); } append(buffer, messageData); diff --git a/src/Solana/Transaction.cpp b/src/Solana/Transaction.cpp index 313f8cc60ac..52ee014c09e 100644 --- a/src/Solana/Transaction.cpp +++ b/src/Solana/Transaction.cpp @@ -17,10 +17,9 @@ namespace TW::Solana { std::string Transaction::serialize() const { Data buffer; - append(buffer, shortVecLength(this->signatures)); - for (auto signature : this->signatures) { - Data signature_vec(signature.bytes.begin(), signature.bytes.end()); - append(buffer, signature_vec); + append(buffer, shortVecLength(this->signatures)); + for (auto &&signature : this->signatures) { + append(buffer, signature); } append(buffer, this->messageData()); diff --git a/src/Solana/Transaction.h b/src/Solana/Transaction.h index 65d24a3bfb0..0608382986b 100644 --- a/src/Solana/Transaction.h +++ b/src/Solana/Transaction.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2022 Trust Wallet. +// Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,7 +8,6 @@ #include "Solana/Address.h" #include "Solana/LegacyMessage.h" -#include "Solana/Signature.h" #include "Data.h" #include "BinaryCoding.h" @@ -20,18 +19,18 @@ namespace TW::Solana { class Transaction { public: // Signatures - std::vector signatures; + std::vector signatures; // The message to sign LegacyMessage message; Transaction(const LegacyMessage& message) : message(message) { - this->signatures.resize(message.header.numRequiredSignatures, Signature(defaultSignature)); + this->signatures.resize(message.header.numRequiredSignatures, defaultSignature); } // Default basic transfer transaction Transaction(const Address& from, const Address& to, uint64_t value, Data recentBlockhash, std::string memo = "", std::vector
references = {}) : message(LegacyMessage::createTransfer(from, to, value, recentBlockhash, memo, references)) { - this->signatures.resize(1, Signature(defaultSignature)); + this->signatures.resize(1, defaultSignature); } public: diff --git a/src/Solana/VersionedTransaction.cpp b/src/Solana/VersionedTransaction.cpp index 439add21b80..c8ff5b1ff7a 100644 --- a/src/Solana/VersionedTransaction.cpp +++ b/src/Solana/VersionedTransaction.cpp @@ -12,10 +12,9 @@ namespace TW::Solana { std::string VersionedTransaction::serialize() const { Data buffer; - append(buffer, shortVecLength(this->signatures)); - for (auto signature : this->signatures) { - Data signature_vec(signature.bytes.begin(), signature.bytes.end()); - append(buffer, signature_vec); + append(buffer, shortVecLength(this->signatures)); + for (auto &&signature : this->signatures) { + append(buffer, signature); } append(buffer, this->messageData()); diff --git a/src/Solana/VersionedTransaction.h b/src/Solana/VersionedTransaction.h index 02af2d377be..01460c79ee1 100644 --- a/src/Solana/VersionedTransaction.h +++ b/src/Solana/VersionedTransaction.h @@ -8,7 +8,6 @@ #include "Solana/Address.h" #include "Solana/VersionedMessage.h" -#include "Solana/Signature.h" #include "Data.h" #include "BinaryCoding.h" @@ -20,18 +19,18 @@ namespace TW::Solana { class VersionedTransaction { public: // Signatures - std::vector signatures; + std::vector signatures; // The message to sign VersionedMessage message; VersionedTransaction(const VersionedMessage& message) : message(message) { - this->signatures.resize(header(message).numRequiredSignatures, Signature(defaultSignature)); + this->signatures.resize(header(message).numRequiredSignatures, defaultSignature); } // Default basic transfer transaction VersionedTransaction(const Address& from, const Address& to, uint64_t value, Data recentBlockhash, std::string memo = "", std::vector
references = {}) : message(VersionedMessage(LegacyMessage::createTransfer(from, to, value, recentBlockhash, memo, references))) { - this->signatures.resize(1, Signature(defaultSignature)); + this->signatures.resize(1, defaultSignature); } public: diff --git a/tests/chains/Solana/SignerTests.cpp b/tests/chains/Solana/SignerTests.cpp index 96bc64a332d..5b828ad7b4a 100644 --- a/tests/chains/Solana/SignerTests.cpp +++ b/tests/chains/Solana/SignerTests.cpp @@ -99,8 +99,8 @@ TEST(SolanaSigner, SingleSignTransaction) { signerKeys.push_back(privateKey); Signer::sign(signerKeys, transaction); - std::vector expectedSignatures; - Signature expectedSignature( + std::vector expectedSignatures; + auto expectedSignature = Base58::decode( "5T6uZBHnHFd8uWErDBTFRVkbKuhbcm94K5MJ2beTYDruzqv4FjS7EMKvC94ZfxNAiWUXZ6bZxS3WXUbhJwYNPWn"); expectedSignatures.push_back(expectedSignature); ASSERT_EQ(transaction.signatures, expectedSignatures); @@ -139,8 +139,8 @@ TEST(SolanaSigner, SignTransactionToSelf) { signerKeys.push_back(privateKey); Signer::sign(signerKeys, transaction); - std::vector expectedSignatures; - Signature expectedSignature( + std::vector expectedSignatures; + auto expectedSignature = Base58::decode( "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); expectedSignatures.push_back(expectedSignature); ASSERT_EQ(transaction.signatures, expectedSignatures); @@ -193,11 +193,11 @@ TEST(SolanaSigner, MultipleSignTransaction) { signerKeys.push_back(privateKey0); Signer::sign(signerKeys, transaction); - std::vector expectedSignatures; - Signature expectedSignature0( + std::vector expectedSignatures; + auto expectedSignature0 = Base58::decode( "37beWPhNMfWUz75Tb24TX3PCS89FZscbCgwwLpFnzVfZYPqDpAWruvqzc9eeQYft35H23Vm9Tv1dPwEKWT3vAVPb"); expectedSignatures.push_back(expectedSignature0); - Signature expectedSignature1( + auto expectedSignature1 = Base58::decode( "5NxQshVaAXtQ8YVdcBtCanT62KbxnRfhubjGndFvetgn9AiaoLVZvRGutR5D7FJebRxq8bd6nQXn59LFzavEUrdQ"); expectedSignatures.push_back(expectedSignature1); ASSERT_EQ(transaction.signatures, expectedSignatures); @@ -230,8 +230,8 @@ TEST(SolanaSigner, SignUpdateBlockhash) { auto newBlockhash = Base58::decode("GgBaCs3NCBuZN12kCJgAW63ydqohFkHEdfdEXBPzLHq"); Signer::signUpdateBlockhash(signerKeys, transaction, newBlockhash); - std::vector expectedSignatures; - Signature expectedSignature( + std::vector expectedSignatures; + auto expectedSignature = Base58::decode( "5AFhXjvGdENXCAe9MPvUA2qjoL4XtZwZKG7kK2HmZf1ibpxjx5kzogHZjN39uYB9J33UFJN15KhSggBZhzyNQmta"); expectedSignatures.push_back(expectedSignature); ASSERT_EQ(transaction.signatures, expectedSignatures); @@ -289,8 +289,8 @@ TEST(SolanaSigner, SignDelegateStakeV2) { signerKeys.push_back(privateKeySigner); Signer::sign(signerKeys, transaction); - std::vector expectedSignatures; - Signature expectedSignature("58iogHzSJZmvTxi71W8k2yZXSPVfGAgtgqrk1RaBtfVFewU9yiJCkvSF1Hhjyax5DuexzR7ryWZDAWKQ73pyqvMs"); + std::vector expectedSignatures; + auto expectedSignature = Base58::decode("58iogHzSJZmvTxi71W8k2yZXSPVfGAgtgqrk1RaBtfVFewU9yiJCkvSF1Hhjyax5DuexzR7ryWZDAWKQ73pyqvMs"); expectedSignatures.push_back(expectedSignature); EXPECT_EQ(transaction.signatures, expectedSignatures); @@ -317,8 +317,8 @@ TEST(SolanaSigner, SignDelegateStakeV1) { signerKeys.push_back(privateKeySigner); Signer::sign(signerKeys, transaction); - std::vector expectedSignatures; - Signature expectedSignature("gDPbnakbktrASmnUwKGpmftvQRbcyAvxyAyVXq3oVLfAdTPDqY8hhLPHTgidEZGWcmiaXnEyKg2GQLkkAh3JYr3"); + std::vector expectedSignatures; + auto expectedSignature = Base58::decode("gDPbnakbktrASmnUwKGpmftvQRbcyAvxyAyVXq3oVLfAdTPDqY8hhLPHTgidEZGWcmiaXnEyKg2GQLkkAh3JYr3"); expectedSignatures.push_back(expectedSignature); EXPECT_EQ(transaction.signatures, expectedSignatures); @@ -344,8 +344,8 @@ TEST(SolanaSigner, SignCreateTokenAccount) { signerKeys.push_back(privateKeySigner); Signer::sign(signerKeys, transaction); - std::vector expectedSignatures; - Signature expectedSignature("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); + std::vector expectedSignatures; + auto expectedSignature = Base58::decode("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); expectedSignatures.push_back(expectedSignature); EXPECT_EQ(transaction.signatures, expectedSignatures); @@ -402,8 +402,8 @@ TEST(SolanaSigner, SignTransferToken_3vZ67C) { signerKeys.push_back(privateKeySigner); Signer::sign(signerKeys, transaction); - std::vector expectedSignatures; - Signature expectedSignature("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); + std::vector expectedSignatures; + auto expectedSignature = Base58::decode("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); expectedSignatures.push_back(expectedSignature); EXPECT_EQ(transaction.signatures, expectedSignatures); diff --git a/tests/chains/Solana/TransactionTests.cpp b/tests/chains/Solana/TransactionTests.cpp index 25bed86af71..d62e5e04368 100644 --- a/tests/chains/Solana/TransactionTests.cpp +++ b/tests/chains/Solana/TransactionTests.cpp @@ -34,8 +34,7 @@ TEST(SolanaTransaction, TransferSerializeTransaction) { auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto transaction = Transaction(from, to, 42, recentBlockhash); - Signature signature( - "46SRiQGvtPb1iivDfnuC3dW1GzXkfQPTjdUyvFqF2sdPvFrsfx94fys2xpNKR6UiAj7RgKWdJG6mEfe85up6i1JT"); + auto signature = Base58::decode("46SRiQGvtPb1iivDfnuC3dW1GzXkfQPTjdUyvFqF2sdPvFrsfx94fys2xpNKR6UiAj7RgKWdJG6mEfe85up6i1JT"); transaction.signatures.clear(); transaction.signatures.push_back(signature); @@ -52,7 +51,7 @@ TEST(SolanaTransaction, TransferTransactionPayToSelf) { auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); auto transaction = Transaction(from, to, 42, recentBlockhash); - Signature signature( + auto signature = Base58::decode( "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); transaction.signatures.clear(); transaction.signatures.push_back(signature); @@ -71,7 +70,7 @@ TEST(SolanaTransaction, TransferWithMemoAndReferenceTransaction) { const auto memo = "HelloSolana73"; std::vector
references = {Address("GaeTAQZyhVEocTC7iY8GztSyY5cBAJTkAUUA1kLFLMV")}; auto transaction = Transaction(from, to, 42, recentBlockhash, memo, references); - const Signature signature("3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); + auto signature = Base58::decode("3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); transaction.signatures.clear(); transaction.signatures.push_back(signature); @@ -87,7 +86,7 @@ TEST(SolanaTransaction, StakeSerializeTransactionV2) { auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); auto transaction = Transaction(message); - Signature signature( + auto signature = Base58::decode( "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); transaction.signatures.clear(); transaction.signatures.push_back(signature); @@ -104,7 +103,7 @@ TEST(SolanaTransaction, StakeSerializeTransactionV1) { auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); auto transaction = Transaction(message); - Signature signature( + auto signature = Base58::decode( "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); transaction.signatures.clear(); transaction.signatures.push_back(signature); @@ -143,7 +142,7 @@ TEST(SolanaTransaction, CreateTokenAccountTransaction) { EXPECT_EQ(message.instructions[0].accounts[6].account.string(), "SysvarRent111111111111111111111111111111111"); auto transaction = Transaction(message); transaction.signatures.clear(); - Signature signature("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); + auto signature = Base58::decode("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); transaction.signatures.push_back(signature); auto expectedString = @@ -170,7 +169,7 @@ TEST(SolanaTransaction, TransferTokenTransaction_3vZ67C) { ASSERT_EQ(message.instructions[0].accounts.size(), 4ul); auto transaction = Transaction(message); transaction.signatures.clear(); - Signature signature("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); + auto signature = Base58::decode("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); transaction.signatures.push_back(signature); auto expectedString = From 55f1225168fe4edc83dae17e745f518aec5f3c63 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 2 Mar 2023 15:27:43 +0100 Subject: [PATCH 113/426] [Tron]: Staking V2 (#2932) --- src/Tron/Protobuf/TronInternal.proto | 38 +++++- src/Tron/Serialization.cpp | 72 ++++++++++++ src/Tron/Signer.cpp | 104 ++++++++++++++++- src/proto/Tron.proto | 61 ++++++++++ tests/chains/Tron/SignerTests.cpp | 166 +++++++++++++++++++++++++++ 5 files changed, 439 insertions(+), 2 deletions(-) diff --git a/src/Tron/Protobuf/TronInternal.proto b/src/Tron/Protobuf/TronInternal.proto index 88bc5675912..f2be7207561 100644 --- a/src/Tron/Protobuf/TronInternal.proto +++ b/src/Tron/Protobuf/TronInternal.proto @@ -17,6 +17,11 @@ message Transaction { WithdrawBalanceContract = 13; UnfreezeAssetContract = 14; TriggerSmartContract = 31; + FreezeBalanceV2Contract = 54; + UnfreezeBalanceV2Contract = 55; + WithdrawExpireUnfreezeContract = 56; + DelegateResourceContract = 57; + UnDelegateResourceContract = 58; } ContractType type = 1; google.protobuf.Any parameter = 2; @@ -78,6 +83,12 @@ message FreezeBalanceContract { bytes receiver_address = 15; } +message FreezeBalanceV2Contract { + bytes owner_address = 1; + int64 frozen_balance = 2; + ResourceCode resource = 3; +} + message UnfreezeBalanceContract { bytes owner_address = 1; @@ -85,6 +96,31 @@ message UnfreezeBalanceContract { bytes receiver_address = 15; } +message UnfreezeBalanceV2Contract { + bytes owner_address = 1; + int64 unfreeze_balance = 2; + ResourceCode resource = 3; +} + +message WithdrawExpireUnfreezeContract { + bytes owner_address = 1; +} + +message DelegateResourceContract { + bytes owner_address = 1; + ResourceCode resource = 2; + int64 balance = 3; + bytes receiver_address = 4; + bool lock = 5; +} + +message UnDelegateResourceContract { + bytes owner_address = 1; + ResourceCode resource = 2; + int64 balance = 3; + bytes receiver_address = 4; +} + message UnfreezeAssetContract { bytes owner_address = 1; } @@ -117,4 +153,4 @@ message TriggerSmartContract { bytes data = 4; int64 call_token_value = 5; int64 token_id = 6; -} \ No newline at end of file +} diff --git a/src/Tron/Serialization.cpp b/src/Tron/Serialization.cpp index 36bfe251f69..270ffb22481 100644 --- a/src/Tron/Serialization.cpp +++ b/src/Tron/Serialization.cpp @@ -93,6 +93,14 @@ json valueJSON(const protocol::FreezeBalanceContract& contract) { return valueJSON; } +json valueJSON(const protocol::FreezeBalanceV2Contract& contract) { + json valueJSON; + valueJSON["owner_address"] = hex(contract.owner_address()); + valueJSON["frozen_balance"] = contract.frozen_balance(); + valueJSON["resource"] = protocol::ResourceCode_Name(contract.resource()); + return valueJSON; +} + json valueJSON(const protocol::UnfreezeBalanceContract& contract) { json valueJSON; valueJSON["owner_address"] = hex(contract.owner_address()); @@ -102,6 +110,40 @@ json valueJSON(const protocol::UnfreezeBalanceContract& contract) { return valueJSON; } +json valueJSON(const protocol::UnfreezeBalanceV2Contract& contract) { + json valueJSON; + valueJSON["owner_address"] = hex(contract.owner_address()); + valueJSON["resource"] = protocol::ResourceCode_Name(contract.resource()); + valueJSON["unfreeze_balance"] = contract.unfreeze_balance(); + + return valueJSON; +} + +json valueJSON(const protocol::DelegateResourceContract& contract) { + json valueJSON; + valueJSON["owner_address"] = hex(contract.owner_address()); + valueJSON["receiver_address"] = hex(contract.receiver_address()); + valueJSON["resource"] = protocol::ResourceCode_Name(contract.resource()); + valueJSON["balance"] = contract.balance(); + valueJSON["lock"] = contract.lock(); + return valueJSON; +} + +json valueJSON(const protocol::UnDelegateResourceContract& contract) { + json valueJSON; + valueJSON["owner_address"] = hex(contract.owner_address()); + valueJSON["receiver_address"] = hex(contract.receiver_address()); + valueJSON["resource"] = protocol::ResourceCode_Name(contract.resource()); + valueJSON["balance"] = contract.balance(); + return valueJSON; +} + +json valueJSON(const protocol::WithdrawExpireUnfreezeContract& contract) { + json valueJSON; + valueJSON["owner_address"] = hex(contract.owner_address()); + return valueJSON; +} + json valueJSON(const protocol::WithdrawBalanceContract& contract) { json valueJSON; valueJSON["owner_address"] = hex(contract.owner_address()); @@ -169,12 +211,42 @@ json parameterJSON(const google::protobuf::Any& parameter, const protocol::Trans paramJSON["value"] = valueJSON(contract); break; } + case protocol::Transaction::Contract::FreezeBalanceV2Contract: { + protocol::FreezeBalanceV2Contract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } case protocol::Transaction::Contract::UnfreezeBalanceContract: { protocol::UnfreezeBalanceContract contract; parameter.UnpackTo(&contract); paramJSON["value"] = valueJSON(contract); break; } + case protocol::Transaction::Contract::UnfreezeBalanceV2Contract: { + protocol::UnfreezeBalanceV2Contract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::WithdrawExpireUnfreezeContract: { + protocol::WithdrawExpireUnfreezeContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::DelegateResourceContract: { + protocol::DelegateResourceContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::UnDelegateResourceContract: { + protocol::UnDelegateResourceContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } case protocol::Transaction::Contract::WithdrawBalanceContract: { protocol::WithdrawBalanceContract contract; parameter.UnpackTo(&contract); diff --git a/src/Tron/Signer.cpp b/src/Tron/Signer.cpp index b3278891aea..e2b4a987399 100644 --- a/src/Tron/Signer.cpp +++ b/src/Tron/Signer.cpp @@ -70,6 +70,20 @@ protocol::FreezeBalanceContract to_internal(const Proto::FreezeBalanceContract& return internal; } +protocol::FreezeBalanceV2Contract to_internal(const Proto::FreezeBalanceV2Contract& freezeContract) { + auto internal = protocol::FreezeBalanceV2Contract(); + auto resource = protocol::ResourceCode(); + const auto ownerAddress = Base58::decodeCheck(freezeContract.owner_address()); + + protocol::ResourceCode_Parse(freezeContract.resource(), &resource); + + internal.set_resource(resource); + internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); + internal.set_frozen_balance(freezeContract.frozen_balance()); + + return internal; +} + protocol::UnfreezeBalanceContract to_internal(const Proto::UnfreezeBalanceContract& unfreezeContract) { auto internal = protocol::UnfreezeBalanceContract(); auto resource = protocol::ResourceCode(); @@ -85,6 +99,60 @@ protocol::UnfreezeBalanceContract to_internal(const Proto::UnfreezeBalanceContra return internal; } +protocol::UnfreezeBalanceV2Contract to_internal(const Proto::UnfreezeBalanceV2Contract& unfreezeContract) { + auto internal = protocol::UnfreezeBalanceV2Contract(); + auto resource = protocol::ResourceCode(); + const auto ownerAddress = Base58::decodeCheck(unfreezeContract.owner_address()); + + protocol::ResourceCode_Parse(unfreezeContract.resource(), &resource); + + internal.set_resource(resource); + internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); + internal.set_unfreeze_balance(unfreezeContract.unfreeze_balance()); + + return internal; +} + +protocol::DelegateResourceContract to_internal(const Proto::DelegateResourceContract& delegateContract) { + auto internal = protocol::DelegateResourceContract(); + auto resource = protocol::ResourceCode(); + const auto ownerAddress = Base58::decodeCheck(delegateContract.owner_address()); + const auto receiverAddress = Base58::decodeCheck(delegateContract.receiver_address()); + + protocol::ResourceCode_Parse(delegateContract.resource(), &resource); + + internal.set_resource(resource); + internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); + internal.set_receiver_address(receiverAddress.data(), receiverAddress.size()); + internal.set_balance(delegateContract.balance()); + internal.set_lock(delegateContract.lock()); + + return internal; +} + +protocol::UnDelegateResourceContract to_internal(const Proto::UnDelegateResourceContract& undelegateContract) { + auto internal = protocol::UnDelegateResourceContract(); + auto resource = protocol::ResourceCode(); + const auto ownerAddress = Base58::decodeCheck(undelegateContract.owner_address()); + const auto receiverAddress = Base58::decodeCheck(undelegateContract.receiver_address()); + + protocol::ResourceCode_Parse(undelegateContract.resource(), &resource); + + internal.set_resource(resource); + internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); + internal.set_receiver_address(receiverAddress.data(), receiverAddress.size()); + internal.set_balance(undelegateContract.balance()); + + return internal; +} + +protocol::WithdrawExpireUnfreezeContract to_internal(const Proto::WithdrawExpireUnfreezeContract& withdrawExpireUnfreezeContract) { + auto internal = protocol::WithdrawExpireUnfreezeContract(); + const auto ownerAddress = Base58::decodeCheck(withdrawExpireUnfreezeContract.owner_address()); + internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); + return internal; +} + protocol::UnfreezeAssetContract to_internal(const Proto::UnfreezeAssetContract& unfreezeContract) { auto internal = protocol::UnfreezeAssetContract(); const auto ownerAddress = Base58::decodeCheck(unfreezeContract.owner_address()); @@ -229,14 +297,48 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { google::protobuf::Any any; any.PackFrom(freeze_balance); *contract->mutable_parameter() = any; + } else if (input.transaction().has_freeze_balance_v2()) { + auto* contract = internal.mutable_raw_data()->add_contract(); + contract->set_type(protocol::Transaction_Contract_ContractType_FreezeBalanceV2Contract); + auto freeze_balance = to_internal(input.transaction().freeze_balance_v2()); + google::protobuf::Any any; + any.PackFrom(freeze_balance); + *contract->mutable_parameter() = any; } else if (input.transaction().has_unfreeze_balance()) { auto* contract = internal.mutable_raw_data()->add_contract(); contract->set_type(protocol::Transaction_Contract_ContractType_UnfreezeBalanceContract); - auto unfreeze_balance = to_internal(input.transaction().unfreeze_balance()); google::protobuf::Any any; any.PackFrom(unfreeze_balance); *contract->mutable_parameter() = any; + } else if (input.transaction().has_unfreeze_balance_v2()) { + auto* contract = internal.mutable_raw_data()->add_contract(); + contract->set_type(protocol::Transaction_Contract_ContractType_UnfreezeBalanceV2Contract); + auto unfreeze_balance = to_internal(input.transaction().unfreeze_balance_v2()); + google::protobuf::Any any; + any.PackFrom(unfreeze_balance); + *contract->mutable_parameter() = any; + } else if (input.transaction().has_withdraw_expire_unfreeze()) { + auto* contract = internal.mutable_raw_data()->add_contract(); + contract->set_type(protocol::Transaction_Contract_ContractType_WithdrawExpireUnfreezeContract); + auto withdraw_expire_unfreeze = to_internal(input.transaction().withdraw_expire_unfreeze()); + google::protobuf::Any any; + any.PackFrom(withdraw_expire_unfreeze); + *contract->mutable_parameter() = any; + } else if (input.transaction().has_delegate_resource()) { + auto* contract = internal.mutable_raw_data()->add_contract(); + contract->set_type(protocol::Transaction_Contract_ContractType_DelegateResourceContract); + auto delegate_resource = to_internal(input.transaction().delegate_resource()); + google::protobuf::Any any; + any.PackFrom(delegate_resource); + *contract->mutable_parameter() = any; + } else if (input.transaction().has_undelegate_resource()) { + auto* contract = internal.mutable_raw_data()->add_contract(); + contract->set_type(protocol::Transaction_Contract_ContractType_UnDelegateResourceContract); + auto undelegate_resource = to_internal(input.transaction().undelegate_resource()); + google::protobuf::Any any; + any.PackFrom(undelegate_resource); + *contract->mutable_parameter() = any; } else if (input.transaction().has_unfreeze_asset()) { auto* contract = internal.mutable_raw_data()->add_contract(); contract->set_type(protocol::Transaction_Contract_ContractType_UnfreezeAssetContract); diff --git a/src/proto/Tron.proto b/src/proto/Tron.proto index 7ed7ce37723..63b9dcd0825 100644 --- a/src/proto/Tron.proto +++ b/src/proto/Tron.proto @@ -63,6 +63,62 @@ message FreezeBalanceContract { string receiver_address = 15; } +// stake TRX to obtain TRON Power (voting rights) and bandwidth or energy. +message FreezeBalanceV2Contract { + // Address of transaction initiator, data type is string + string owner_address = 1; + + // Amount of TRX to be staked, unit is sun, data type is uint256 + int64 frozen_balance = 2; + + // Resource type, "BANDWIDTH" or "ENERGY", data type is string + string resource = 3; +} + +// Unstake TRX to release bandwidth and energy and at the same time TRON Power will be reduced and all corresponding votes will be canceled. +message UnfreezeBalanceV2Contract { + // Address of transaction initiator, data type is string + string owner_address = 1; + // Amount of TRX to be unstaked, unit is sun, data type is uint256 + int64 unfreeze_balance = 2; + // Resource type, "BANDWIDTH" or "ENERGY", data type is string + string resource = 3; +} + +// withdraw unfrozen balance +message WithdrawExpireUnfreezeContract { + // Address of transaction initiator, data type is string + string owner_address = 1; +} + +// delegate resource +message DelegateResourceContract { + // Address of transaction initiator, data type is string + string owner_address = 1; + // Resource type, "BANDWIDTH" or "ENERGY", data type is string + string resource = 2; + // Amount of TRX staked for resource to be delegated, unit is sun, data type is uint256 + int64 balance = 3; + // Receiver address of resource to be delegated to + string receiver_address = 4; + // Whether it is locked, if it is set to true, the delegated resources cannot be undelegated within 3 days. + // When the lock time is not over, if the owner delegates the same resources using the lock to the same address, + // the lock time will be reset to 3 days + bool lock = 5; +} + +// undelegate resource +message UnDelegateResourceContract { + // Address of transaction initiator, data type is string + string owner_address = 1; + // Resource type, "BANDWIDTH" or "ENERGY", data type is string + string resource = 2; + // Amount of TRX staked for resource to be undelegated, unit is sun, data type is uint256 + int64 balance = 3; + // Receiver address of resource to be delegated to, data type is string + string receiver_address = 4; +} + // Unfreeze balance message UnfreezeBalanceContract { // Sender address @@ -185,6 +241,11 @@ message Transaction { VoteWitnessContract vote_witness = 17; TriggerSmartContract trigger_smart_contract = 18; TransferTRC20Contract transfer_trc20_contract = 19; + FreezeBalanceV2Contract freeze_balance_v2 = 20; + UnfreezeBalanceV2Contract unfreeze_balance_v2 = 21; + WithdrawExpireUnfreezeContract withdraw_expire_unfreeze = 23; + DelegateResourceContract delegate_resource = 24; + UnDelegateResourceContract undelegate_resource = 25; } } diff --git a/tests/chains/Tron/SignerTests.cpp b/tests/chains/Tron/SignerTests.cpp index 69dc5c37cb5..bd7246d46d8 100644 --- a/tests/chains/Tron/SignerTests.cpp +++ b/tests/chains/Tron/SignerTests.cpp @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #include "Bitcoin/Address.h" +#include "Tron/Address.h" #include "HexCoding.h" #include "PrivateKey.h" #include "uint256.h" @@ -80,6 +81,171 @@ TEST(TronSigner, SignTransfer) { ASSERT_EQ(hex(output.signature()), "ede769f6df28aefe6a846be169958c155e23e7e5c9621d2e8dce1719b4d952b63e8a8bf9f00e41204ac1bf69b1a663dacdf764367e48e4a5afcd6b055a747fb200"); } +TEST(TronSigner, SignFreezeBalanceV2) { + // Successfully broadcasted https://nile.tronscan.org/#/transaction/3a46321487ce1fd115da38b3431006ea529f65ef2507f19233f5a23c05abd01d + auto input = Proto::SigningInput(); + auto& transaction = *input.mutable_transaction(); + + auto& freeze = *transaction.mutable_freeze_balance_v2(); + freeze.set_owner_address("TWWb9EjUWai17YEVB7FR8hreupYJKG9sMR"); + freeze.set_frozen_balance(10000000); + freeze.set_resource("ENERGY"); + + transaction.set_timestamp(1676983541337); + transaction.set_expiration(1676983599000); + + auto& blockHeader = *transaction.mutable_block_header(); + blockHeader.set_timestamp(1676983485000); + const auto txTrieRoot = parse_hex("9b54db7f84bd19bbad9ff1fccef894c1aade6879450e9e9e2accec751eaa1f52"); + blockHeader.set_tx_trie_root(txTrieRoot.data(), txTrieRoot.size()); + const auto parentHash = parse_hex("00000000020cd4c13a67497a3a433a3105bc5a73a041ee3da98407d5a2a2bf1b"); + blockHeader.set_parent_hash(parentHash.data(), parentHash.size()); + blockHeader.set_number(34395330); + const auto witnessAddress = parse_hex("4150d3765e4e670727ebac9d5b598f74b75a3d54a7"); + blockHeader.set_witness_address(witnessAddress.data(), witnessAddress.size()); + blockHeader.set_version(26); + + const auto privateKey = PrivateKey(parse_hex("75065f100e38d3f3b4c5c4235834ba8216de62272a4f03532c44b31a5734360a")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.id()), "3a46321487ce1fd115da38b3431006ea529f65ef2507f19233f5a23c05abd01d"); + ASSERT_EQ(hex(output.signature()), "d4b539a389f6721b4e9d0eb9f39b62a539069060e1af2a118f06b81737ad9cdb49d5b4fda85f10603012f8de3996da2a1234c21d74ac6ea5e60217d3c10b630900"); +} + +TEST(TronSigner, WithdrawExpireUnfreezeContract) { + // Successfully broadcasted https://nile.tronscan.org/#/transaction/65ff34192eebda9ba7013771ff2da1010615e348b70c046647f41afe865f00eb + auto input = Proto::SigningInput(); + auto& transaction = *input.mutable_transaction(); + + auto& freeze = *transaction.mutable_withdraw_expire_unfreeze(); + freeze.set_owner_address("TWWb9EjUWai17YEVB7FR8hreupYJKG9sMR"); + + transaction.set_timestamp(1677574466457); + transaction.set_expiration(1677574524000); + + auto& blockHeader = *transaction.mutable_block_header(); + blockHeader.set_timestamp(1677574410000); + const auto txTrieRoot = parse_hex("0000000000000000000000000000000000000000000000000000000000000000"); + blockHeader.set_tx_trie_root(txTrieRoot.data(), txTrieRoot.size()); + const auto parentHash = parse_hex("00000000020fce45738ef00be07c350c03d027851308bc19d61c32312c673d3d"); + blockHeader.set_parent_hash(parentHash.data(), parentHash.size()); + blockHeader.set_number(34590278); + const auto witnessAddress = parse_hex("41e7860196ad5b5718c1d6326babab039b70b8c1cd"); + blockHeader.set_witness_address(witnessAddress.data(), witnessAddress.size()); + blockHeader.set_version(27); + + const auto privateKey = PrivateKey(parse_hex("75065f100e38d3f3b4c5c4235834ba8216de62272a4f03532c44b31a5734360a")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.id()), "65ff34192eebda9ba7013771ff2da1010615e348b70c046647f41afe865f00eb"); + ASSERT_EQ(hex(output.signature()), "ef0361248c118b8afae9c4c8e6dfad1e63eec4fb6c182ae369fa3bbecc2ac29a292838949ad74300b2b7322a110ffd4458224e283181cf6d64df0324b068bb0001"); +} + +TEST(TronSigner, SignUnFreezeBalanceV2) { + // Successfully broadcasted https://nile.tronscan.org/#/transaction/3070adc1743e6fdd20e04a749cc2af691ca26d2ce70e40cc0886be03595f9eeb + auto input = Proto::SigningInput(); + auto& transaction = *input.mutable_transaction(); + + auto& freeze = *transaction.mutable_unfreeze_balance_v2(); + freeze.set_owner_address("TWWb9EjUWai17YEVB7FR8hreupYJKG9sMR"); + freeze.set_unfreeze_balance(510000000); + freeze.set_resource("ENERGY"); + + transaction.set_timestamp(1676992267490); + transaction.set_expiration(1676992326000); + + auto& blockHeader = *transaction.mutable_block_header(); + blockHeader.set_timestamp(1676992212000); + const auto txTrieRoot = parse_hex("4b1edc58d14a5c60c083365d8b77771ba626394b445c7a7b8b5d67330bb6c92d"); + blockHeader.set_tx_trie_root(txTrieRoot.data(), txTrieRoot.size()); + const auto parentHash = parse_hex("00000000020ce000354fbb346d676de268b3f83124381f8496835afe88da4a01"); + blockHeader.set_parent_hash(parentHash.data(), parentHash.size()); + blockHeader.set_number(34398209); + const auto witnessAddress = parse_hex("4194a21bec5d0e1dde2151475f72ed158a87eb4817"); + blockHeader.set_witness_address(witnessAddress.data(), witnessAddress.size()); + blockHeader.set_version(26); + + const auto privateKey = PrivateKey(parse_hex("75065f100e38d3f3b4c5c4235834ba8216de62272a4f03532c44b31a5734360a")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.id()), "3070adc1743e6fdd20e04a749cc2af691ca26d2ce70e40cc0886be03595f9eeb"); + ASSERT_EQ(hex(output.signature()), "10bc05c47102f1db1a3a4c0b4a6aba028d5a35dda4e505563c3f0ccf95a562cf18b53f7f7053c485299cfc599a432d1f0ee5554a56cd5981ccfff31d79b9868b00"); +} + +TEST(TronSigner, DelegateResourceContract) { + // Successfully broadcasted https://nile.tronscan.org/#/transaction/ceabcd0f105854c13aae12ba35c0766945713c29cee540be1239bb0f1f0cde2c + auto input = Proto::SigningInput(); + auto& transaction = *input.mutable_transaction(); + + auto& freeze = *transaction.mutable_delegate_resource(); + freeze.set_owner_address("TWWb9EjUWai17YEVB7FR8hreupYJKG9sMR"); + freeze.set_receiver_address("TPFfHr1CWfTcS9eugQXQmvqHNGufnjxjXP"); + freeze.set_balance(68000000); + freeze.set_resource("ENERGY"); + + transaction.set_timestamp(1676991607274); + transaction.set_expiration(1676991660000); + + auto& blockHeader = *transaction.mutable_block_header(); + blockHeader.set_timestamp(1676991546000); + const auto txTrieRoot = parse_hex("0000000000000000000000000000000000000000000000000000000000000000"); + blockHeader.set_tx_trie_root(txTrieRoot.data(), txTrieRoot.size()); + const auto parentHash = parse_hex("00000000020cdf260ff2357d814141106c375c101913c933c2b5c31a390db7fc"); + blockHeader.set_parent_hash(parentHash.data(), parentHash.size()); + blockHeader.set_number(34397991); + const auto witnessAddress = parse_hex("417d3601dbd9d033b034c154868acc2904d9c45565"); + blockHeader.set_witness_address(witnessAddress.data(), witnessAddress.size()); + blockHeader.set_version(26); + + const auto privateKey = PrivateKey(parse_hex("75065f100e38d3f3b4c5c4235834ba8216de62272a4f03532c44b31a5734360a")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.id()), "ceabcd0f105854c13aae12ba35c0766945713c29cee540be1239bb0f1f0cde2c"); + ASSERT_EQ(hex(output.signature()), "664500a76466497a442cecc0e9282a9234483f047c12a997b6206d7f6a9030c70b700c879d7948c4cbdfe339c2c81a29dea18e00e9916504196c1b20cf045ca300"); +} + +TEST(TronSigner, UnDelegateResourceContract) { + // Successfully broadcasted https://nile.tronscan.org/#/transaction/3609519cc700cf2446b5e048864abc4b45e2ba6b7f9f8890d471ba2876599d3b + auto input = Proto::SigningInput(); + auto& transaction = *input.mutable_transaction(); + + auto& freeze = *transaction.mutable_undelegate_resource(); + freeze.set_owner_address("TWWb9EjUWai17YEVB7FR8hreupYJKG9sMR"); + freeze.set_receiver_address("TPFfHr1CWfTcS9eugQXQmvqHNGufnjxjXP"); + freeze.set_balance(68000000); + freeze.set_resource("ENERGY"); + + transaction.set_timestamp(1676992063012); + transaction.set_expiration(1676992122000); + + auto& blockHeader = *transaction.mutable_block_header(); + blockHeader.set_timestamp(1676992008000); + const auto txTrieRoot = parse_hex("85a47017a4380e92d09bac0f8991031e8de13b8b65767a6f5372d3f0992eabcd"); + blockHeader.set_tx_trie_root(txTrieRoot.data(), txTrieRoot.size()); + const auto parentHash = parse_hex("00000000020cdfbe4d7f36fcbb3d96dd634987b897eaf885001dd62fd92eb263"); + blockHeader.set_parent_hash(parentHash.data(), parentHash.size()); + blockHeader.set_number(34398143); + const auto witnessAddress = parse_hex("4196409f85790883057edf03286d08e4aa608c0d0a"); + blockHeader.set_witness_address(witnessAddress.data(), witnessAddress.size()); + blockHeader.set_version(26); + + const auto privateKey = PrivateKey(parse_hex("75065f100e38d3f3b4c5c4235834ba8216de62272a4f03532c44b31a5734360a")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.id()), "3609519cc700cf2446b5e048864abc4b45e2ba6b7f9f8890d471ba2876599d3b"); + ASSERT_EQ(hex(output.signature()), "b08e32a704d5a366df499d283d407c428dd50e60665f54ecf967226b75bec37157e6bc23312af07fad9dd3551cd668ce027cc280932fd4772af89d6f0fecf11900"); +} + TEST(TronSigner, SignFreezeBalance) { auto input = Proto::SigningInput(); auto& transaction = *input.mutable_transaction(); From 366f65cd610238d460769d3efb98ce7dafca856c Mon Sep 17 00:00:00 2001 From: hewigovens <360470+hewigovens@users.noreply.github.com> Date: Thu, 2 Mar 2023 23:28:43 +0900 Subject: [PATCH 114/426] Add Kotlin Multiplatform sample (#2960) --- .github/workflows/kotlin-ci.yml | 7 + README.md | 7 +- samples/kmp/.gitignore | 10 + samples/kmp/androidApp/build.gradle.kts | 49 +++ .../androidApp/src/main/AndroidManifest.xml | 17 + .../example/kmpsample/android/MainActivity.kt | 40 ++ .../kmpsample/android/MyApplicationTheme.kt | 55 +++ .../androidApp/src/main/res/values/styles.xml | 3 + samples/kmp/build.gradle.kts | 11 + samples/kmp/gradle.properties | 13 + samples/kmp/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + samples/kmp/gradlew | 185 ++++++++ samples/kmp/gradlew.bat | 89 ++++ samples/kmp/iosApp/Podfile | 5 + samples/kmp/iosApp/Podfile.lock | 31 ++ .../iosApp/iosApp.xcodeproj/project.pbxproj | 399 ++++++++++++++++++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++++ .../iosApp/Assets.xcassets/Contents.json | 6 + samples/kmp/iosApp/iosApp/ContentView.swift | 16 + samples/kmp/iosApp/iosApp/Info.plist | 48 +++ .../Preview Assets.xcassets/Contents.json | 6 + samples/kmp/iosApp/iosApp/iOSApp.swift | 10 + samples/kmp/settings.gradle.kts | 37 ++ samples/kmp/shared/build.gradle.kts | 76 ++++ samples/kmp/shared/shared.podspec | 39 ++ .../kotlin/com/example/kmpsample/Platform.kt | 10 + .../kotlin/com/example/kmpsample/Platform.kt | 7 + .../kotlin/com/example/kmpsample/Sample.kt | 17 + .../kotlin/com/example/kmpsample/Platform.kt | 9 + 33 files changed, 1334 insertions(+), 1 deletion(-) create mode 100644 samples/kmp/.gitignore create mode 100644 samples/kmp/androidApp/build.gradle.kts create mode 100644 samples/kmp/androidApp/src/main/AndroidManifest.xml create mode 100644 samples/kmp/androidApp/src/main/java/com/example/kmpsample/android/MainActivity.kt create mode 100644 samples/kmp/androidApp/src/main/java/com/example/kmpsample/android/MyApplicationTheme.kt create mode 100644 samples/kmp/androidApp/src/main/res/values/styles.xml create mode 100644 samples/kmp/build.gradle.kts create mode 100644 samples/kmp/gradle.properties create mode 100644 samples/kmp/gradle/wrapper/gradle-wrapper.jar create mode 100644 samples/kmp/gradle/wrapper/gradle-wrapper.properties create mode 100755 samples/kmp/gradlew create mode 100644 samples/kmp/gradlew.bat create mode 100644 samples/kmp/iosApp/Podfile create mode 100644 samples/kmp/iosApp/Podfile.lock create mode 100644 samples/kmp/iosApp/iosApp.xcodeproj/project.pbxproj create mode 100644 samples/kmp/iosApp/iosApp.xcworkspace/contents.xcworkspacedata create mode 100644 samples/kmp/iosApp/iosApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 samples/kmp/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 samples/kmp/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 samples/kmp/iosApp/iosApp/Assets.xcassets/Contents.json create mode 100644 samples/kmp/iosApp/iosApp/ContentView.swift create mode 100644 samples/kmp/iosApp/iosApp/Info.plist create mode 100644 samples/kmp/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 samples/kmp/iosApp/iosApp/iOSApp.swift create mode 100644 samples/kmp/settings.gradle.kts create mode 100644 samples/kmp/shared/build.gradle.kts create mode 100644 samples/kmp/shared/shared.podspec create mode 100644 samples/kmp/shared/src/androidMain/kotlin/com/example/kmpsample/Platform.kt create mode 100644 samples/kmp/shared/src/commonMain/kotlin/com/example/kmpsample/Platform.kt create mode 100644 samples/kmp/shared/src/commonMain/kotlin/com/example/kmpsample/Sample.kt create mode 100644 samples/kmp/shared/src/iosMain/kotlin/com/example/kmpsample/Platform.kt diff --git a/.github/workflows/kotlin-ci.yml b/.github/workflows/kotlin-ci.yml index 3e88aa6aa45..6522f2224d3 100644 --- a/.github/workflows/kotlin-ci.yml +++ b/.github/workflows/kotlin-ci.yml @@ -46,3 +46,10 @@ jobs: - name: Build Kotlin Multiplatform run: tools/kotlin-build + + - name: Build KMP Sample + run: ./gradlew assemble + working-directory: samples/kmp + env: + GITHUB_USER: ${{ github.actor }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 2b39eab1cdc..91c36caa788 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ Swift for iOS and Java (Kotlin) for Android. ![Android CI](https://github.com/trustwallet/wallet-core/workflows/Android%20CI/badge.svg) ![Linux CI](https://github.com/trustwallet/wallet-core/workflows/Linux%20CI/badge.svg) ![Wasm CI](https://github.com/trustwallet/wallet-core/workflows/Wasm%20CI/badge.svg) +![Kotlin CI](https://github.com/trustwallet/wallet-core/workflows/Kotlin%20CI/badge.svg) ![Docker CI](https://github.com/trustwallet/wallet-core/workflows/Docker%20CI/badge.svg) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=TrustWallet_wallet-core&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=TrustWallet_wallet-core) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/trustwallet/wallet-core) ![GitHub](https://img.shields.io/github/license/TrustWallet/wallet-core.svg) @@ -62,7 +64,7 @@ Or add remote url + `master` branch, it points to recent (not always latest) bin .package(name: "WalletCore", url: "https://github.com/trustwallet/wallet-core", .branchItem("master")), ``` -Then add libraries to target's `dependencies`: +Then add libraries to target's `dependencies`: ```swift .product(name: "WalletCore", package: "WalletCore"), @@ -87,6 +89,9 @@ npm install @trustwallet/wallet-core Please check out the [Go integration sample](https://github.com/trustwallet/wallet-core/tree/master/samples/go). +## Kotlin Multipleplatform (beta) + +Please check out the [Kotlin Multiplatform sample](https://github.com/trustwallet/wallet-core/tree/master/samples/kmp) # Projects diff --git a/samples/kmp/.gitignore b/samples/kmp/.gitignore new file mode 100644 index 00000000000..e510fa99de5 --- /dev/null +++ b/samples/kmp/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +.idea +.DS_Store +build +captures +.externalNativeBuild +.cxx +local.properties +xcuserdata \ No newline at end of file diff --git a/samples/kmp/androidApp/build.gradle.kts b/samples/kmp/androidApp/build.gradle.kts new file mode 100644 index 00000000000..24d6a52c14b --- /dev/null +++ b/samples/kmp/androidApp/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + id("com.android.application") + kotlin("android") +} + +android { + namespace = "com.example.kmpsample.android" + compileSdk = 33 + defaultConfig { + applicationId = "com.example.kmpsample.android" + minSdk = 24 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.4.0" + } + packagingOptions { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(project(":shared")) + implementation("androidx.compose.ui:ui:1.3.1") + implementation("androidx.compose.ui:ui-tooling:1.3.1") + implementation("androidx.compose.ui:ui-tooling-preview:1.3.1") + implementation("androidx.compose.foundation:foundation:1.3.1") + implementation("androidx.compose.material:material:1.3.1") + implementation("androidx.activity:activity-compose:1.6.1") +} \ No newline at end of file diff --git a/samples/kmp/androidApp/src/main/AndroidManifest.xml b/samples/kmp/androidApp/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..22d1facc822 --- /dev/null +++ b/samples/kmp/androidApp/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/kmp/androidApp/src/main/java/com/example/kmpsample/android/MainActivity.kt b/samples/kmp/androidApp/src/main/java/com/example/kmpsample/android/MainActivity.kt new file mode 100644 index 00000000000..1ab49166253 --- /dev/null +++ b/samples/kmp/androidApp/src/main/java/com/example/kmpsample/android/MainActivity.kt @@ -0,0 +1,40 @@ +package com.example.kmpsample.android + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.example.kmpsample.Sample + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + MyApplicationTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colors.background + ) { + GreetingView(Sample().greet()) + } + } + } + } +} + +@Composable +fun GreetingView(text: String) { + Text(text = text) +} + +@Preview +@Composable +fun DefaultPreview() { + MyApplicationTheme { + GreetingView("Hello, Android!") + } +} diff --git a/samples/kmp/androidApp/src/main/java/com/example/kmpsample/android/MyApplicationTheme.kt b/samples/kmp/androidApp/src/main/java/com/example/kmpsample/android/MyApplicationTheme.kt new file mode 100644 index 00000000000..a0929dcde96 --- /dev/null +++ b/samples/kmp/androidApp/src/main/java/com/example/kmpsample/android/MyApplicationTheme.kt @@ -0,0 +1,55 @@ +package com.example.kmpsample.android + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Shapes +import androidx.compose.material.Typography +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun MyApplicationTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + val colors = if (darkTheme) { + darkColors( + primary = Color(0xFFBB86FC), + primaryVariant = Color(0xFF3700B3), + secondary = Color(0xFF03DAC5) + ) + } else { + lightColors( + primary = Color(0xFF6200EE), + primaryVariant = Color(0xFF3700B3), + secondary = Color(0xFF03DAC5) + ) + } + val typography = Typography( + body1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ) + ) + val shapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(4.dp), + large = RoundedCornerShape(0.dp) + ) + + MaterialTheme( + colors = colors, + typography = typography, + shapes = shapes, + content = content + ) +} diff --git a/samples/kmp/androidApp/src/main/res/values/styles.xml b/samples/kmp/androidApp/src/main/res/values/styles.xml new file mode 100644 index 00000000000..6b4fa3d08e0 --- /dev/null +++ b/samples/kmp/androidApp/src/main/res/values/styles.xml @@ -0,0 +1,3 @@ + +