Skip to content

Commit 9e18c93

Browse files
Woo-inghewigovens
authored andcommitted
Raw transaction for Nebulas (#567)
* First commit for Nebulas's functions. * rename TWNebulasAddress.cpp * Fixed the building errors. * First passed all test. * Revert protobuf link to protobuf-3.7.0 * Add nebulas raw transaction data support. * signinginput support pass a payload string. * test for escaped label
1 parent e5bf89b commit 9e18c93

File tree

13 files changed

+258
-31
lines changed

13 files changed

+258
-31
lines changed

android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nebulas/TestNebulasSigner.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class TestNebulasSigner {
2626
gasPrice = ByteString.copyFrom("0x0f4240".toHexByteArray()) //1000000
2727
gasLimit = ByteString.copyFrom("0x030d40".toHexByteArray()) //200000
2828
amount = ByteString.copyFrom("0x98a7d9b8314c0000".toHexByteArray()) //11000000000000000000
29+
payload = ""
2930
timestamp = ByteString.copyFrom("0x5cfc84ca".toHexByteArray()) //1560052938
30-
payload = ByteString.copyFrom("\n\u0006binary".toByteArray()) //{10, 6, 98, 105, 110, 97, 114, 121}
3131
privateKey = ByteString.copyFrom(PrivateKey("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b".toHexByteArray()).data())
3232
}
3333

@@ -36,5 +36,7 @@ class TestNebulasSigner {
3636
assertEquals(output.algorithm, 1)
3737
assertEquals(output.signature.toByteArray().toHex(),
3838
"0xf53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500")
39+
assertEquals(output.raw,
40+
"CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA=")
3941
}
40-
}
42+
}

include/TrustWalletCore/TWNebulasProto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010

1111
typedef TWData *_Nonnull TW_Nebulas_Proto_SigningInput;
1212
typedef TWData *_Nonnull TW_Nebulas_Proto_SigningOutput;
13+
typedef TWData *_Nonnull TW_Nebulas_Proto_Data;
14+
typedef TWData *_Nonnull TW_Nebulas_Proto_RawTransaction;

js/tests/blockchain/nebulas/NebulasSigner.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('NebulasSigner', () => {
1313
gasLimit: fromHexString('0x030d40'), //200000
1414
gasPrice: fromHexString('0x0f4240'), //1000000
1515
nonce: fromHexString('0x7'), //7
16-
payload: new Uint8Array((('\n\x06binary').match(/[\s\S]/g)||[]).map(ch=>ch.charCodeAt(0))), //{10, 6, 98, 105, 110, 97, 114, 121}
16+
payload: '',
1717
privateKey: PrivateKey.createWithData(fromHexString("0xd2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")).data(),
1818
timestamp: fromHexString("0x5cfc84ca"), //1560052938
1919
toAddress: "n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17",
@@ -22,6 +22,7 @@ describe('NebulasSigner', () => {
2222

2323
expect(output.algorithm == 1);
2424
expect(bufToHex(output.signature)).to.equal('0xf53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500');
25+
expect(output.raw == 'CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA=');
2526
});
2627

2728
});

src/Coin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey) {
300300

301301
case TWCoinTypeWaves:
302302
return Waves::Address(publicKey).string();
303-
303+
304304
case TWCoinTypeNebulas:
305305
return Nebulas::Address(publicKey).string();
306306
}

src/Nebulas/Signer.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
// file LICENSE at the root of the source code distribution tree.
66

77
#include "Signer.h"
8+
#include "Base64.h"
89
#include "../HexCoding.h"
910

1011
using namespace TW;
1112
using namespace TW::Nebulas;
1213

13-
const char *Signer::TxPayloadBinaryType = "binary";
14-
const char *Signer::TxPayloadDeployType = "deploy";
15-
const char *Signer::TxPayloadCallType = "call";
16-
1714
Proto::SigningOutput Signer::sign(Proto::SigningInput &input) const noexcept {
1815
Transaction tx(Address(input.from_address()),
1916
load(input.nonce()),
@@ -22,7 +19,7 @@ Proto::SigningOutput Signer::sign(Proto::SigningInput &input) const noexcept {
2219
Address(input.to_address()),
2320
load(input.amount()),
2421
load(input.timestamp()),
25-
Data(input.payload().begin(), input.payload().end())
22+
input.payload()
2623
);
2724

2825
auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end()));
@@ -31,25 +28,34 @@ Proto::SigningOutput Signer::sign(Proto::SigningInput &input) const noexcept {
3128
auto protoOutput = Proto::SigningOutput();
3229
protoOutput.set_algorithm(tx.algorithm);
3330
protoOutput.set_signature(reinterpret_cast<const char *>(tx.signature.data()), tx.signature.size());
31+
protoOutput.set_raw(TW::Base64::encode(tx.raw));
3432
return protoOutput;
3533
}
3634

3735
void Signer::sign(const PrivateKey &privateKey, Transaction &transaction) const noexcept {
38-
auto hash = this->hash(transaction);
36+
transaction.hash = this->hash(transaction);
37+
transaction.chainID = chainID;
3938
transaction.algorithm = 1;
40-
transaction.signature = privateKey.sign(hash, TWCurveSECP256k1);
39+
transaction.signature = privateKey.sign(transaction.hash, TWCurveSECP256k1);
40+
transaction.serializeToRaw();
4141
}
4242

4343
Data Signer::hash(const Transaction &transaction) const noexcept {
4444
auto encoded = Data();
45+
auto payload = Data();
46+
auto data = Transaction::newPayloadData(transaction.payload);
47+
payload.resize(data->ByteSize());
48+
data->SerializePartialToArray(payload.data(),(int)payload.size());
49+
delete data;
50+
4551
encoded.insert(encoded.end(), transaction.from.bytes.begin(), transaction.from.bytes.end());
4652
encoded.insert(encoded.end(), transaction.to.bytes.begin(), transaction.to.bytes.end());
4753
encode256BE(encoded, transaction.amount, 128);
4854
encode256BE(encoded, transaction.nonce, 64);
4955
encode256BE(encoded, transaction.timestamp, 64);
50-
encoded.insert(encoded.end(), transaction.payload.begin(), transaction.payload.end());
56+
encoded.insert(encoded.end(), payload.begin(), payload.end());
5157
encode256BE(encoded, chainID, 32);
5258
encode256BE(encoded, transaction.gasPrice, 128);
5359
encode256BE(encoded, transaction.gasLimit, 128);
5460
return Hash::sha3_256(encoded);
55-
}
61+
}

src/Nebulas/Signer.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ namespace TW::Nebulas {
2424
class Signer {
2525
public:
2626
uint256_t chainID;
27-
28-
static const char* TxPayloadBinaryType;
29-
static const char* TxPayloadDeployType;
30-
static const char* TxPayloadCallType;
3127

3228
/// Initializes a signer with a chain identifier.
3329
explicit Signer(uint256_t chainID) : chainID(std::move(chainID)) {}
@@ -48,4 +44,4 @@ class Signer {
4844
/// Wrapper for C interface.
4945
struct TWNebulasSigner {
5046
TW::Nebulas::Signer impl;
51-
};
47+
};

src/Nebulas/Transaction.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright © 2017-2019 Trust Wallet.
2+
//
3+
// This file is part of Trust. The full Trust copyright notice, including
4+
// Copyright © 2017-2019 Trust Wallet.
5+
//
6+
// This file is part of Trust. The full Trust copyright notice, including
7+
// terms governing use, modification, and redistribution, is contained in the
8+
// file LICENSE at the root of the source code distribution tree.
9+
#include <nlohmann/json.hpp>
10+
#include "Transaction.h"
11+
#include "../HexCoding.h"
12+
13+
using namespace TW;
14+
using namespace TW::Nebulas;
15+
16+
const char *Transaction::TxPayloadBinaryType = "binary";
17+
const char *Transaction::TxPayloadDeployType = "deploy";
18+
const char *Transaction::TxPayloadCallType = "call";
19+
20+
std::string htmlescape(const std::string& str) {
21+
std::string result;
22+
for(size_t i=0; i<str.size(); ++i) {
23+
switch(str[i])
24+
{
25+
case '&': result += "\\u0026"; break;
26+
case '>': result += "\\u003e"; break;
27+
case '<': result += "\\u003c"; break;
28+
case 0x20:
29+
if(i+1 < str.size()) {
30+
if(str[i+1]==0x28) {
31+
result += "\\u2028";
32+
++i;
33+
break;
34+
}
35+
else if (str[i+1]==0x29) {
36+
result += "\\u2029";
37+
++i;
38+
break;
39+
}
40+
}
41+
default: result += str[i]; break;
42+
}
43+
}
44+
return result;
45+
}
46+
47+
Proto::Data* Transaction::newPayloadData(const std::string& payload){
48+
auto data = new Proto::Data();
49+
data->set_type(Transaction::TxPayloadBinaryType);
50+
51+
nlohmann::json payloadData;
52+
if(!payload.empty()) {
53+
auto json = nlohmann::json::parse(payload);
54+
if(json.find("binary")!=json.end()) {
55+
std::string binary_data = json["binary"];
56+
auto buff = Data(binary_data.begin(),binary_data.end());
57+
payloadData["Data"]["type"] = "Buffer";
58+
payloadData["Data"]["data"] = nlohmann::json(buff);
59+
}
60+
}
61+
if(!payloadData.empty())
62+
data->set_payload(htmlescape(payloadData.dump()));
63+
return data;
64+
}
65+
66+
void Transaction::serializeToRaw(){
67+
if(signature.empty()) {
68+
throw std::logic_error("The transaction is unsigned!");
69+
}
70+
71+
auto tx = Proto::RawTransaction();
72+
auto data = newPayloadData(payload);
73+
74+
auto value = Data();
75+
auto gas_price = Data();
76+
auto gas_limit = Data();
77+
tx.set_hash(reinterpret_cast<const char *>(hash.data()),hash.size());
78+
tx.set_from(from.bytes.data(),from.size);
79+
tx.set_to(to.bytes.data(),to.size);
80+
encode256BE(value, amount, 128);
81+
tx.set_value(value.data(),value.size());
82+
tx.set_nonce((uint64_t)nonce);
83+
tx.set_timestamp((int64_t)timestamp);
84+
tx.set_allocated_data(data);
85+
tx.set_chain_id((uint32_t)chainID);
86+
encode256BE(gas_price, gasPrice, 128);
87+
tx.set_gas_price(gas_price.data(),gas_price.size());
88+
encode256BE(gas_limit, gasLimit, 128);
89+
tx.set_gas_limit(gas_limit.data(),gas_limit.size());
90+
91+
tx.set_alg((uint32_t)algorithm);
92+
tx.set_sign(reinterpret_cast<const char *>(signature.data()),signature.size());
93+
94+
raw.resize(tx.ByteSize());
95+
tx.SerializeToArray(raw.data(),(int)raw.size());
96+
}

src/Nebulas/Transaction.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,49 @@
88

99
#include "Address.h"
1010
#include "../uint256.h"
11+
#include "../proto/Nebulas.pb.h"
1112

1213
namespace TW::Nebulas {
1314

1415
class Transaction {
1516
public:
17+
static const char* TxPayloadBinaryType;
18+
static const char* TxPayloadDeployType;
19+
static const char* TxPayloadCallType;
20+
1621
Address from;
1722
uint256_t nonce;
1823
uint256_t gasPrice;
1924
uint256_t gasLimit;
2025
Address to;
2126
uint256_t amount;
2227
uint256_t timestamp;
23-
std::vector<uint8_t> payload;
28+
std::string payload;
2429

2530
// Signature values
31+
uint256_t chainID;
32+
Data hash;
2633
Data signature;
2734
uint32_t algorithm;
2835

29-
Transaction(Address from, uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, Address to, uint256_t amount, uint256_t timestamp, Data payload)
36+
// serialize data
37+
Data raw;
38+
39+
Transaction(Address from, uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, Address to, uint256_t amount, uint256_t timestamp, const std::string& payload)
3040
: from(std::move(from))
3141
, nonce(std::move(nonce))
3242
, gasPrice(std::move(gasPrice))
3343
, gasLimit(std::move(gasLimit))
3444
, to(std::move(to))
3545
, amount(std::move(amount))
3646
, timestamp(std::move(timestamp))
37-
, payload(std::move(payload)){}
47+
, payload(payload){}
48+
49+
public:
50+
static Proto::Data* newPayloadData(const std::string& payload);
51+
52+
///serialize the signed transaction.
53+
void serializeToRaw();
3854
};
3955

40-
} // namespace TW::Nebulas
56+
} // namespace TW::Nebulas

src/proto/Nebulas.proto

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ message SigningInput {
3131
bytes timestamp = 8;
3232

3333
// Optional payload
34-
bytes payload = 9;
34+
string payload = 9;
3535

3636
// Private key.
3737
bytes private_key = 10;
@@ -41,4 +41,28 @@ message SigningInput {
4141
message SigningOutput {
4242
uint32 algorithm = 1;
4343
bytes signature = 2;
44+
string raw = 3;
4445
}
46+
47+
//
48+
message Data {
49+
string type = 1;
50+
bytes payload = 2;
51+
}
52+
53+
// Raw transaction data
54+
message RawTransaction {
55+
bytes hash = 1;
56+
bytes from = 2;
57+
bytes to = 3;
58+
bytes value = 4;
59+
uint64 nonce = 5;
60+
int64 timestamp = 6;
61+
Data data = 7;
62+
uint32 chain_id = 8;
63+
bytes gas_price = 9;
64+
bytes gas_limit = 10;
65+
66+
uint32 alg = 11;
67+
bytes sign = 12;
68+
}

swift/Tests/Blockchains/NebulasTests.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ class NebulasTests: XCTestCase {
2626
$0.gasLimit = Data(hexString: "030d40")! //200000
2727
$0.toAddress = "n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"
2828
$0.amount = Data(hexString: "98a7d9b8314c0000")! //11000000000000000000ULL
29+
$0.payload = ""
2930
$0.timestamp = Data(hexString: "5cfc84ca")! //1560052938
30-
$0.payload = "\n\u{6}binary".data(using: String.Encoding.utf8)! //{10, 6, 98, 105, 110, 97, 114, 121}
3131
$0.privateKey = PrivateKey(data: Data(hexString: "d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")!)!.data
3232
}
3333
let output = NebulasSigner.sign(input: input)
3434
XCTAssertEqual(output.algorithm, 1)
35-
XCTAssertEqual(output.signature.hexString,
36-
"f53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500")
35+
//swiftlint:disable:next line_length
36+
XCTAssertEqual(output.signature.hexString, "f53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500")
37+
//swiftlint:disable:next line_length
38+
XCTAssertEqual(output.raw, "CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA=")
3739
}
3840
}

tests/Nebulas/SignerTests.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ TEST(NebulasSigner, EmptyData) {
2828
ASSERT_EQ(zero, uint256_t(0));
2929
}
3030

31-
const char* payloadString = "\n\6binary"; //{10, 6, 98, 105, 110, 97, 114, 121}
3231
TEST(NebulasSigner, Hash) {
3332
auto from = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY");
3433
auto to = Address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17");
@@ -40,7 +39,7 @@ TEST(NebulasSigner, Hash) {
4039
/* to: */ to,
4140
/* amount: */ 11000000000000000000ULL,
4241
/* timestamp: */ 1560052938,
43-
/* payload: */ Data(payloadString,payloadString+8));
42+
/* payload: */ std::string());
4443
auto signer = SignerExposed(1);
4544
auto hash = signer.hash(transaction);
4645

@@ -59,9 +58,9 @@ TEST(NebulasSigner, Sign) {
5958
input.set_gas_limit(value.data(),value.size());
6059
value = store(uint256_t(11000000000000000000ULL));
6160
input.set_amount(value.data(),value.size());
61+
input.set_payload("");
6262
value = store(uint256_t(1560052938));
6363
input.set_timestamp(value.data(),value.size());
64-
input.set_payload(payloadString);
6564

6665
const auto privateKey = PrivateKey(parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b"));
6766
input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size());
@@ -73,4 +72,4 @@ TEST(NebulasSigner, Sign) {
7372
ASSERT_EQ(hex(signature.begin(),signature.end()), "f53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500");
7473
}
7574

76-
} // namespace TW::Nebulas
75+
} // namespace TW::Nebulas

0 commit comments

Comments
 (0)