Skip to content

Commit

Permalink
Waves tx json serialization (#526)
Browse files Browse the repository at this point in the history
* added waves tx json serialization

* review fixes

* json dump test
  • Loading branch information
Tolsi authored and hewigovens committed Jun 21, 2019
1 parent a718618 commit 3f77d9c
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@ class TestWavesTransactionSigner {
val signingInput = Waves.SigningInput.newBuilder()
signingInput.apply {
amount = 100_000_000
amountAsset = "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"
asset = "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"
fee = 100_000
feeAsset = "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"
to = "3PPCZQkvdMJpmx7Zrz1cnYsPe9Bt1XT2Ckx"
attachment = ByteString.copyFrom("68656c6c6f".toHexByteArray())
timestamp = 1559146613
publicKey = ByteString.copyFrom("794e8429ddf58353eacab99b4c2b3b71c8c1b5927980db2d6f8db52744dd103d".toHexByteArray())
privateKey = ByteString.copyFrom("68b7a9adb4a655b205f43dac413803785921e22cd7c4d05857b203a62621075f".toHexByteArray())
}

Expand Down
3 changes: 1 addition & 2 deletions js/tests/blockchain/waves/WavesSigner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ describe('WavesSigner', () => {
it('test Waves transaction signing', () => {
const signingInput = TW.Waves.Proto.SigningInput.create({
amount: Long.fromString('100000000'),
amountAsset: 'DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq',
asset: 'DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq',
attachment: fromHexString('68656c6c6f'),
fee: Long.fromString('100000'),
feeAsset: 'DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq',
privateKey: fromHexString('68b7a9adb4a655b205f43dac413803785921e22cd7c4d05857b203a62621075f'),
publicKey: fromHexString('794e8429ddf58353eacab99b4c2b3b71c8c1b5927980db2d6f8db52744dd103d'),
timestamp: Long.fromString('1559146613'),
to: '3PPCZQkvdMJpmx7Zrz1cnYsPe9Bt1XT2Ckx',
});
Expand Down
28 changes: 26 additions & 2 deletions src/Waves/Transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
using namespace TW;
using namespace TW::Waves;

using json = nlohmann::json;

const std::string Transaction::WAVES = "WAVES";

Data Transaction::serializeToSign() const {
Expand All @@ -28,11 +30,11 @@ Data Transaction::serializeToSign() const {
data[1] = static_cast<byte>(TransactionVersion::V2);
append(data, pub_key);

if (amount_asset == WAVES) {
if (asset == WAVES) {
data.push_back(static_cast<uint8_t>(0));
} else {
data.push_back(static_cast<uint8_t>(1));
append(data, Base58::bitcoin.decode(amount_asset));
append(data, Base58::bitcoin.decode(asset));
}

if (fee_asset == WAVES) {
Expand All @@ -48,4 +50,26 @@ Data Transaction::serializeToSign() const {
append(data, Data(std::begin(to.bytes), std::end(to.bytes)));
encodeDynamicLengthBytes(attachment, data);
return data;
}

json Transaction::buildJson(Data signature) const {
json jsonTx;

jsonTx["type"] = TransactionType::transfer;
jsonTx["version"] = TransactionVersion::V2;
jsonTx["fee"] = fee;
jsonTx["senderPublicKey"] = Base58::bitcoin.encode(pub_key);
jsonTx["timestamp"] = timestamp;
jsonTx["proofs"] = json::array({Base58::bitcoin.encode(signature)}).dump();
jsonTx["recipient"] = Address(to).string();
if (asset != WAVES) {
jsonTx["assetId"] = asset;
}
if (fee_asset != WAVES) {
jsonTx["feeAssetId"] = fee_asset;
}
jsonTx["amount"] = amount;
jsonTx["attachment"] = Base58::bitcoin.encode(attachment);

return jsonTx;
}
12 changes: 7 additions & 5 deletions src/Waves/Transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "Address.h"
#include "../Data.h"
#include "../proto/Waves.pb.h"
#include <nlohmann/json.hpp>

namespace TW::Waves {

Expand All @@ -23,26 +24,26 @@ class Transaction {
static const std::string WAVES;

int64_t amount;
std::string amount_asset;
std::string asset;
int64_t fee;
std::string fee_asset;
Address to;
Data attachment;
int64_t timestamp;
Data pub_key;

Transaction(int64_t amount, std::string amount_asset, int64_t fee, std::string fee_asset,
Transaction(int64_t amount, std::string asset, int64_t fee, std::string fee_asset,
Address to, Data attachment, int64_t timestamp, Data pub_key)
: amount(std::move(amount))
, fee(std::move(fee))
, to(std::move(to))
, attachment(std::move(attachment))
, timestamp(std::move(timestamp))
, pub_key(std::move(pub_key)) {
if (amount_asset.empty()) {
amount_asset = WAVES;
if (asset.empty()) {
asset = WAVES;
}
this->amount_asset = amount_asset;
this->asset = asset;
if (fee_asset.empty()) {
fee_asset = WAVES;
}
Expand All @@ -51,6 +52,7 @@ class Transaction {

public:
Data serializeToSign() const;
nlohmann::json buildJson(Data signature) const;
};

} // namespace TW::Waves
11 changes: 7 additions & 4 deletions src/interface/TWWavesSigner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <TrustWalletCore/TWWavesSigner.h>

#include "../Base58.h"
#include "../Waves/Signer.h"
#include "../proto/Waves.pb.h"

Expand All @@ -16,21 +17,23 @@ TW_Waves_Proto_SigningOutput TWWavesSignerSign(TW_Waves_Proto_SigningInput data)
Proto::SigningInput input;
input.ParseFromArray(TWDataBytes(data), static_cast<int>(TWDataSize(data)));

auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end()));
auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end()));
auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519);
auto transaction = Transaction(
/* amount */ input.amount(),
/* amount_asset */ input.amount_asset(),
/* asset */ input.asset(),
/* fee */ input.fee(),
/* fee_asset */ input.fee_asset(),
/* to */ Address(input.to()),
/* attachment */ Data(input.attachment().begin(), input.attachment().end()),
/* timestamp */ input.timestamp(),
/* pub_key */ Data(input.public_key().begin(), input.public_key().end()));
/* pub_key */ publicKey.bytes);

Data signature = Signer::sign(key, transaction);
Data signature = Signer::sign(privateKey, transaction);

Proto::SigningOutput protoOutput = Proto::SigningOutput();
protoOutput.set_signature(reinterpret_cast<const char *>(signature.data()), signature.size());
protoOutput.set_json(transaction.buildJson(signature).dump());
std::string serialized = protoOutput.SerializeAsString();
return TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(serialized.data()),
serialized.size());
Expand Down
9 changes: 5 additions & 4 deletions src/proto/Waves.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ option java_package = "wallet.core.jni.proto";
message SigningInput {
int64 amount = 1;

string amount_asset = 2;
string asset = 2;

// minimum 0.001 Waves (100000 Wavelets) for now
int64 fee = 3;

string fee_asset = 4;
Expand All @@ -18,14 +19,14 @@ message SigningInput {
// any 140 bytes payload, will be displayed to the client as utf-8 string
bytes attachment = 6;

// in millis
int64 timestamp = 7;

bytes public_key = 8;

bytes private_key = 9;
bytes private_key = 8;
}

// Transaction signing output.
message SigningOutput {
bytes signature = 1;
string json = 2;
}
42 changes: 42 additions & 0 deletions tests/Waves/TransactionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
#include "Waves/Address.h"
#include "Waves/Transaction.h"

#include <Waves/Signer.h>
#include <gtest/gtest.h>

using json = nlohmann::json;

using namespace std;
using namespace TW;
using namespace TW::Waves;
Expand Down Expand Up @@ -67,4 +70,43 @@ TEST(WavesTransaction, failedSerialize) {
/* pub_key */
parse_hex("d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef22"));
EXPECT_THROW(tx1.serializeToSign(), invalid_argument);
}

TEST(WavesTransaction, jsonSerialize) {
const auto privateKey =
PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a"));
const auto publicKeyCurve25519 = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519);
ASSERT_EQ(hex(Data(publicKeyCurve25519.bytes.begin(), publicKeyCurve25519.bytes.end())),
"559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d");
// 3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds
const auto address = Address(publicKeyCurve25519);

auto tx1 = Transaction(
/* amount */ 10000000,
/* amount asset */ "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq",
/* fee */ 100000000,
/* fee asset*/ "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq",
/* to */ address,
/* attachment */ parse_hex("66616c6166656c"),
/* timestamp */ 1526641218066,
/* pub_key */
parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"));

auto signature = Signer::sign(privateKey, tx1);

auto json = tx1.buildJson(signature);

ASSERT_EQ(json["type"], TransactionType::transfer);
ASSERT_EQ(json["version"], TransactionVersion::V2);
ASSERT_EQ(json["fee"], 100000000);
ASSERT_EQ(json["senderPublicKey"], "6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU");
ASSERT_EQ(json["timestamp"], 1526641218066);
ASSERT_EQ(json["proofs"], "[\"5ynN2NUiFHkQzw9bK8R7dZcNfTWMAtcWRJsrMvFFM6dUT3fSnPCCX7CTajNU8bJCB"
"H69vU1mnwfx4zpDtF1SkzKg\"]");
ASSERT_EQ(json["recipient"], "3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds");
ASSERT_EQ(json["assetId"], "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq");
ASSERT_EQ(json["feeAssetId"], "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq");
ASSERT_EQ(json["amount"], 10000000);
ASSERT_EQ(json["attachment"], "4t2Xazb2SX");
ASSERT_EQ(json.dump(), "{\"amount\":10000000,\"assetId\":\"DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq\",\"attachment\":\"4t2Xazb2SX\",\"fee\":100000000,\"feeAssetId\":\"DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq\",\"proofs\":\"[\\\"5ynN2NUiFHkQzw9bK8R7dZcNfTWMAtcWRJsrMvFFM6dUT3fSnPCCX7CTajNU8bJCBH69vU1mnwfx4zpDtF1SkzKg\\\"]\",\"recipient\":\"3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds\",\"senderPublicKey\":\"6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU\",\"timestamp\":1526641218066,\"type\":4,\"version\":2}");
}
7 changes: 3 additions & 4 deletions tests/interface/TWWavesSignerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ TEST(TWWavesSigner, Sign) {
const auto publicKeyCurve25519 = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519);

input.set_amount(int64_t(100000000));
input.set_amount_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq");
input.set_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq");
input.set_fee(int64_t(100000));
input.set_fee_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq");
input.set_to("3PPCZQkvdMJpmx7Zrz1cnYsPe9Bt1XT2Ckx");
input.set_attachment("hello");
input.set_timestamp(int64_t(1559146613));
input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size());
input.set_public_key(publicKeyCurve25519.bytes.data(), publicKeyCurve25519.bytes.size());

auto inputData = input.SerializeAsString();
auto inputTWData = TWDataCreateWithBytes((const byte *)inputData.data(), inputData.size());
Expand All @@ -48,13 +47,13 @@ TEST(TWWavesSigner, Sign) {

auto transaction = Waves::Transaction(
/* amount */ input.amount(),
/* amount_asset */ input.amount_asset(),
/* asset */ input.asset(),
/* fee */ input.fee(),
/* fee_asset */ input.fee_asset(),
/* to */ Waves::Address(input.to()),
/* attachment */ Data(input.attachment().begin(), input.attachment().end()),
/* timestamp */ input.timestamp(),
/* pub_key */ Data(input.public_key().begin(), input.public_key().end()));
/* pub_key */ publicKeyCurve25519.bytes);

ASSERT_TRUE(publicKeyCurve25519.verify(
Data(output.signature().begin(), output.signature().end()), transaction.serializeToSign()));
Expand Down

0 comments on commit 3f77d9c

Please sign in to comment.