Skip to content

Commit

Permalink
Raw transaction for Nebulas (#567)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Woo-ing authored and hewigovens committed Aug 6, 2019
1 parent e5bf89b commit 9e18c93
Show file tree
Hide file tree
Showing 13 changed files with 258 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class TestNebulasSigner {
gasPrice = ByteString.copyFrom("0x0f4240".toHexByteArray()) //1000000
gasLimit = ByteString.copyFrom("0x030d40".toHexByteArray()) //200000
amount = ByteString.copyFrom("0x98a7d9b8314c0000".toHexByteArray()) //11000000000000000000
payload = ""
timestamp = ByteString.copyFrom("0x5cfc84ca".toHexByteArray()) //1560052938
payload = ByteString.copyFrom("\n\u0006binary".toByteArray()) //{10, 6, 98, 105, 110, 97, 114, 121}
privateKey = ByteString.copyFrom(PrivateKey("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b".toHexByteArray()).data())
}

Expand All @@ -36,5 +36,7 @@ class TestNebulasSigner {
assertEquals(output.algorithm, 1)
assertEquals(output.signature.toByteArray().toHex(),
"0xf53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500")
assertEquals(output.raw,
"CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA=")
}
}
}
2 changes: 2 additions & 0 deletions include/TrustWalletCore/TWNebulasProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@

typedef TWData *_Nonnull TW_Nebulas_Proto_SigningInput;
typedef TWData *_Nonnull TW_Nebulas_Proto_SigningOutput;
typedef TWData *_Nonnull TW_Nebulas_Proto_Data;
typedef TWData *_Nonnull TW_Nebulas_Proto_RawTransaction;
3 changes: 2 additions & 1 deletion js/tests/blockchain/nebulas/NebulasSigner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('NebulasSigner', () => {
gasLimit: fromHexString('0x030d40'), //200000
gasPrice: fromHexString('0x0f4240'), //1000000
nonce: fromHexString('0x7'), //7
payload: new Uint8Array((('\n\x06binary').match(/[\s\S]/g)||[]).map(ch=>ch.charCodeAt(0))), //{10, 6, 98, 105, 110, 97, 114, 121}
payload: '',
privateKey: PrivateKey.createWithData(fromHexString("0xd2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")).data(),
timestamp: fromHexString("0x5cfc84ca"), //1560052938
toAddress: "n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17",
Expand All @@ -22,6 +22,7 @@ describe('NebulasSigner', () => {

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

});
2 changes: 1 addition & 1 deletion src/Coin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey) {

case TWCoinTypeWaves:
return Waves::Address(publicKey).string();

case TWCoinTypeNebulas:
return Nebulas::Address(publicKey).string();
}
Expand Down
24 changes: 15 additions & 9 deletions src/Nebulas/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
// file LICENSE at the root of the source code distribution tree.

#include "Signer.h"
#include "Base64.h"
#include "../HexCoding.h"

using namespace TW;
using namespace TW::Nebulas;

const char *Signer::TxPayloadBinaryType = "binary";
const char *Signer::TxPayloadDeployType = "deploy";
const char *Signer::TxPayloadCallType = "call";

Proto::SigningOutput Signer::sign(Proto::SigningInput &input) const noexcept {
Transaction tx(Address(input.from_address()),
load(input.nonce()),
Expand All @@ -22,7 +19,7 @@ Proto::SigningOutput Signer::sign(Proto::SigningInput &input) const noexcept {
Address(input.to_address()),
load(input.amount()),
load(input.timestamp()),
Data(input.payload().begin(), input.payload().end())
input.payload()
);

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

void Signer::sign(const PrivateKey &privateKey, Transaction &transaction) const noexcept {
auto hash = this->hash(transaction);
transaction.hash = this->hash(transaction);
transaction.chainID = chainID;
transaction.algorithm = 1;
transaction.signature = privateKey.sign(hash, TWCurveSECP256k1);
transaction.signature = privateKey.sign(transaction.hash, TWCurveSECP256k1);
transaction.serializeToRaw();
}

Data Signer::hash(const Transaction &transaction) const noexcept {
auto encoded = Data();
auto payload = Data();
auto data = Transaction::newPayloadData(transaction.payload);
payload.resize(data->ByteSize());
data->SerializePartialToArray(payload.data(),(int)payload.size());
delete data;

encoded.insert(encoded.end(), transaction.from.bytes.begin(), transaction.from.bytes.end());
encoded.insert(encoded.end(), transaction.to.bytes.begin(), transaction.to.bytes.end());
encode256BE(encoded, transaction.amount, 128);
encode256BE(encoded, transaction.nonce, 64);
encode256BE(encoded, transaction.timestamp, 64);
encoded.insert(encoded.end(), transaction.payload.begin(), transaction.payload.end());
encoded.insert(encoded.end(), payload.begin(), payload.end());
encode256BE(encoded, chainID, 32);
encode256BE(encoded, transaction.gasPrice, 128);
encode256BE(encoded, transaction.gasLimit, 128);
return Hash::sha3_256(encoded);
}
}
6 changes: 1 addition & 5 deletions src/Nebulas/Signer.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ namespace TW::Nebulas {
class Signer {
public:
uint256_t chainID;

static const char* TxPayloadBinaryType;
static const char* TxPayloadDeployType;
static const char* TxPayloadCallType;

/// Initializes a signer with a chain identifier.
explicit Signer(uint256_t chainID) : chainID(std::move(chainID)) {}
Expand All @@ -48,4 +44,4 @@ class Signer {
/// Wrapper for C interface.
struct TWNebulasSigner {
TW::Nebulas::Signer impl;
};
};
96 changes: 96 additions & 0 deletions src/Nebulas/Transaction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright © 2017-2019 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// Copyright © 2017-2019 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.
#include <nlohmann/json.hpp>
#include "Transaction.h"
#include "../HexCoding.h"

using namespace TW;
using namespace TW::Nebulas;

const char *Transaction::TxPayloadBinaryType = "binary";
const char *Transaction::TxPayloadDeployType = "deploy";
const char *Transaction::TxPayloadCallType = "call";

std::string htmlescape(const std::string& str) {
std::string result;
for(size_t i=0; i<str.size(); ++i) {
switch(str[i])
{
case '&': result += "\\u0026"; break;
case '>': result += "\\u003e"; break;
case '<': result += "\\u003c"; break;
case 0x20:
if(i+1 < str.size()) {
if(str[i+1]==0x28) {
result += "\\u2028";
++i;
break;
}
else if (str[i+1]==0x29) {
result += "\\u2029";
++i;
break;
}
}
default: result += str[i]; break;
}
}
return result;
}

Proto::Data* Transaction::newPayloadData(const std::string& payload){
auto data = new Proto::Data();
data->set_type(Transaction::TxPayloadBinaryType);

nlohmann::json payloadData;
if(!payload.empty()) {
auto json = nlohmann::json::parse(payload);
if(json.find("binary")!=json.end()) {
std::string binary_data = json["binary"];
auto buff = Data(binary_data.begin(),binary_data.end());
payloadData["Data"]["type"] = "Buffer";
payloadData["Data"]["data"] = nlohmann::json(buff);
}
}
if(!payloadData.empty())
data->set_payload(htmlescape(payloadData.dump()));
return data;
}

void Transaction::serializeToRaw(){
if(signature.empty()) {
throw std::logic_error("The transaction is unsigned!");
}

auto tx = Proto::RawTransaction();
auto data = newPayloadData(payload);

auto value = Data();
auto gas_price = Data();
auto gas_limit = Data();
tx.set_hash(reinterpret_cast<const char *>(hash.data()),hash.size());
tx.set_from(from.bytes.data(),from.size);
tx.set_to(to.bytes.data(),to.size);
encode256BE(value, amount, 128);
tx.set_value(value.data(),value.size());
tx.set_nonce((uint64_t)nonce);
tx.set_timestamp((int64_t)timestamp);
tx.set_allocated_data(data);
tx.set_chain_id((uint32_t)chainID);
encode256BE(gas_price, gasPrice, 128);
tx.set_gas_price(gas_price.data(),gas_price.size());
encode256BE(gas_limit, gasLimit, 128);
tx.set_gas_limit(gas_limit.data(),gas_limit.size());

tx.set_alg((uint32_t)algorithm);
tx.set_sign(reinterpret_cast<const char *>(signature.data()),signature.size());

raw.resize(tx.ByteSize());
tx.SerializeToArray(raw.data(),(int)raw.size());
}
24 changes: 20 additions & 4 deletions src/Nebulas/Transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,49 @@

#include "Address.h"
#include "../uint256.h"
#include "../proto/Nebulas.pb.h"

namespace TW::Nebulas {

class Transaction {
public:
static const char* TxPayloadBinaryType;
static const char* TxPayloadDeployType;
static const char* TxPayloadCallType;

Address from;
uint256_t nonce;
uint256_t gasPrice;
uint256_t gasLimit;
Address to;
uint256_t amount;
uint256_t timestamp;
std::vector<uint8_t> payload;
std::string payload;

// Signature values
uint256_t chainID;
Data hash;
Data signature;
uint32_t algorithm;

Transaction(Address from, uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, Address to, uint256_t amount, uint256_t timestamp, Data payload)
// serialize data
Data raw;

Transaction(Address from, uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, Address to, uint256_t amount, uint256_t timestamp, const std::string& payload)
: from(std::move(from))
, nonce(std::move(nonce))
, gasPrice(std::move(gasPrice))
, gasLimit(std::move(gasLimit))
, to(std::move(to))
, amount(std::move(amount))
, timestamp(std::move(timestamp))
, payload(std::move(payload)){}
, payload(payload){}

public:
static Proto::Data* newPayloadData(const std::string& payload);

///serialize the signed transaction.
void serializeToRaw();
};

} // namespace TW::Nebulas
} // namespace TW::Nebulas
26 changes: 25 additions & 1 deletion src/proto/Nebulas.proto
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ message SigningInput {
bytes timestamp = 8;

// Optional payload
bytes payload = 9;
string payload = 9;

// Private key.
bytes private_key = 10;
Expand All @@ -41,4 +41,28 @@ message SigningInput {
message SigningOutput {
uint32 algorithm = 1;
bytes signature = 2;
string raw = 3;
}

//
message Data {
string type = 1;
bytes payload = 2;
}

// Raw transaction data
message RawTransaction {
bytes hash = 1;
bytes from = 2;
bytes to = 3;
bytes value = 4;
uint64 nonce = 5;
int64 timestamp = 6;
Data data = 7;
uint32 chain_id = 8;
bytes gas_price = 9;
bytes gas_limit = 10;

uint32 alg = 11;
bytes sign = 12;
}
8 changes: 5 additions & 3 deletions swift/Tests/Blockchains/NebulasTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ class NebulasTests: XCTestCase {
$0.gasLimit = Data(hexString: "030d40")! //200000
$0.toAddress = "n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"
$0.amount = Data(hexString: "98a7d9b8314c0000")! //11000000000000000000ULL
$0.payload = ""
$0.timestamp = Data(hexString: "5cfc84ca")! //1560052938
$0.payload = "\n\u{6}binary".data(using: String.Encoding.utf8)! //{10, 6, 98, 105, 110, 97, 114, 121}
$0.privateKey = PrivateKey(data: Data(hexString: "d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")!)!.data
}
let output = NebulasSigner.sign(input: input)
XCTAssertEqual(output.algorithm, 1)
XCTAssertEqual(output.signature.hexString,
"f53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500")
//swiftlint:disable:next line_length
XCTAssertEqual(output.signature.hexString, "f53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500")
//swiftlint:disable:next line_length
XCTAssertEqual(output.raw, "CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA=")
}
}
7 changes: 3 additions & 4 deletions tests/Nebulas/SignerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ TEST(NebulasSigner, EmptyData) {
ASSERT_EQ(zero, uint256_t(0));
}

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

Expand All @@ -59,9 +58,9 @@ TEST(NebulasSigner, Sign) {
input.set_gas_limit(value.data(),value.size());
value = store(uint256_t(11000000000000000000ULL));
input.set_amount(value.data(),value.size());
input.set_payload("");
value = store(uint256_t(1560052938));
input.set_timestamp(value.data(),value.size());
input.set_payload(payloadString);

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

} // namespace TW::Nebulas
} // namespace TW::Nebulas
Loading

1 comment on commit 9e18c93

@chetlahet001
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.