Skip to content

Commit

Permalink
[Nimiq]: Add support for Nimiq PoS Albatross Mainnet (#4115)
Browse files Browse the repository at this point in the history
* Add support for Nimiq Albatross PoS transaction serialization (#4110)

* Update Nimiq Transaction encoding for Albatross PoS

* Update tests

* [Nimiq]: Add networkId parameter to SigningInput

* [Nimiq]: Fix iOS test

* [Nimiq]: Fix invalid integer type cast

* [Nimiq]: Fix iOS test

---------

Co-authored-by: Sören Schwert <hello@soerenschwert.de>
  • Loading branch information
satoshiotomakan and sisou authored Nov 18, 2024
1 parent 3772536 commit 45124dd
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/Nimiq/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept {
/* destination */Address(input.destination()),
/* amount */input.value(),
/* fee */input.fee(),
/* vsh */input.validity_start_height()
/* vsh */input.validity_start_height(),
/* networkId */input.network_id()
);

auto signer = Signer();
Expand Down
36 changes: 33 additions & 3 deletions src/Nimiq/Transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,33 @@

namespace TW::Nimiq {

const uint8_t NETWORK_ID = 42;
const uint8_t EMPTY_FLAGS = 0;

std::vector<uint8_t> Transaction::serialize() const {
// Source code:
// https://github.com/nimiq/core-rs-albatross/blame/b8ed402c9096ffb54afea52347b91ab7831e75de/primitives/transaction/src/lib.rs#L699

std::vector<uint8_t> data;

data.push_back(0x00); // Basic TX type
if (isAlbatross()) {
data.push_back(0x00); // Signature Proof type and flags (Ed25519 type and no flags)
}
data.insert(data.end(), sender_pub_key.begin(), sender_pub_key.end());
data.insert(data.end(), destination.bytes.begin(), destination.bytes.end());
encode64BE(amount, data);
encode64BE(fee, data);
encode32BE(vsh, data);
data.push_back(NETWORK_ID);
data.push_back(consensusNetworkId());
data.insert(data.end(), signature.begin(), signature.end());

return data;
}

std::vector<uint8_t> Transaction::getPreImage() const {
// Source code:
// https://github.com/nimiq/core-rs-albatross/blame/b8ed402c9096ffb54afea52347b91ab7831e75de/primitives/transaction/src/lib.rs#L582

std::vector<uint8_t> data;

// Build pre-image
Expand All @@ -40,10 +48,32 @@ std::vector<uint8_t> Transaction::getPreImage() const {
encode64BE(amount, data);
encode64BE(fee, data);
encode32BE(vsh, data);
data.push_back(NETWORK_ID);
data.push_back(consensusNetworkId());
data.push_back(EMPTY_FLAGS);
if (isAlbatross()) {
data.push_back(0x00); // Sender Data size (+ 0 bytes of data)
}

return data;
}

bool Transaction::isAlbatross() const {
if (networkId == Proto::NetworkId::MainnetAlbatross) {
return true;
}
return false;
}

uint8_t Transaction::consensusNetworkId() const {
switch (networkId) {
case Proto::NetworkId::UseDefault:
case Proto::NetworkId::Mainnet:
return static_cast<uint8_t>(Proto::NetworkId::Mainnet);
case Proto::NetworkId::MainnetAlbatross:
return static_cast<uint8_t>(Proto::NetworkId::MainnetAlbatross);
default:
throw std::invalid_argument("Invalid network ID");
}
}

} // namespace TW::Nimiq
11 changes: 9 additions & 2 deletions src/Nimiq/Transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "Address.h"
#include "proto/Nimiq.pb.h"

namespace TW::Nimiq {

Expand All @@ -20,16 +21,22 @@ class Transaction {
uint64_t fee;
// Validity start (block) height
uint32_t vsh;
// Network ID
Proto::NetworkId networkId;
// Sender signature
std::array<uint8_t, 64> signature;

Transaction(const std::array<uint8_t, 32>& sender, const Address& dest, uint64_t amount,
uint64_t fee, uint32_t vsh)
: sender_pub_key(sender), destination(dest), amount(amount), fee(fee), vsh(vsh) {}
uint64_t fee, uint32_t vsh, Proto::NetworkId networkId)
: sender_pub_key(sender), destination(dest), amount(amount), fee(fee), vsh(vsh), networkId(networkId) {}

public:
std::vector<uint8_t> serialize() const;
std::vector<uint8_t> getPreImage() const;

private:
bool isAlbatross() const;
uint8_t consensusNetworkId() const;
};

} // namespace TW::Nimiq
11 changes: 11 additions & 0 deletions src/proto/Nimiq.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ syntax = "proto3";
package TW.Nimiq.Proto;
option java_package = "wallet.core.jni.proto";

enum NetworkId {
UseDefault = 0;
// Default PoW Mainnet.
Mainnet = 42;
// PoS Mainnet starting at the PoW block height 3’456’000.
MainnetAlbatross = 24;
}

// Input data necessary to create a signed transaction.
message SigningInput {
// The secret private key used for signing (32 bytes).
Expand All @@ -19,6 +27,9 @@ message SigningInput {

// Validity start, in block height
uint32 validity_start_height = 5;

// Network ID.
NetworkId network_id = 6;
}

// Result containing the signed and encoded transaction.
Expand Down
3 changes: 2 additions & 1 deletion swift/Tests/Blockchains/NimiqTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ class NimiqTests: XCTestCase {
$0.value = 42042042
$0.fee = 1000
$0.validityStartHeight = 314159
$0.networkID = .mainnetAlbatross
}

let output: NimiqSigningOutput = AnySigner.sign(input: input, coin: .nimiq)

XCTAssertEqual(output.encoded.hexString, "0070c7492aaa9c9ac7a05bc0d9c5db2dae9372029654f71f0c7f95deed5099b7021450ffc385cd4e7c6ac9a7e91614ca67ff90568a00000000028182ba00000000000003e80004cb2f2a74dc7f6e0ab58a0bf52cc6e8801b0cca132dd4229d9a3e3a3d2f90e4d8f045d981b771bf5fc3851a98f3c617b1a943228f963e910e061808a721cfa0e3cad50b")
XCTAssertEqual(output.encoded.hexString, "000070c7492aaa9c9ac7a05bc0d9c5db2dae9372029654f71f0c7f95deed5099b7021450ffc385cd4e7c6ac9a7e91614ca67ff90568a00000000028182ba00000000000003e80004cb2f180ba678744be3bf9cd44fbcdabfb5be209f21739934836e26055610ab02720fa99489219d9f3581664473a1b40b30ad1f6e13150d59f8234a42c3f0de3d505405")
}
}
3 changes: 2 additions & 1 deletion tests/chains/Nimiq/SignerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ TEST(NimiqSigner, Sign) {
Address("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA"),
42042042,
1000,
314159
314159,
Proto::NetworkId::Mainnet
);

Signer signer;
Expand Down
18 changes: 18 additions & 0 deletions tests/chains/Nimiq/TWAnySignerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,22 @@ TEST(TWAnySignerNimiq, Sign) {
EXPECT_EQ(hex(output.encoded()), "0070c7492aaa9c9ac7a05bc0d9c5db2dae9372029654f71f0c7f95deed5099b7021450ffc385cd4e7c6ac9a7e91614ca67ff90568a00000000028182ba00000000000003e80004cb2f2a74dc7f6e0ab58a0bf52cc6e8801b0cca132dd4229d9a3e3a3d2f90e4d8f045d981b771bf5fc3851a98f3c617b1a943228f963e910e061808a721cfa0e3cad50b");
}

TEST(TWAnySignerNimiq, SignPoS) {
auto privateKey = parse_hex("e3cc33575834add098f8487123cd4bca543ee859b3e8cfe624e7e6a97202b756");

Proto::SigningInput input;

input.set_destination("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA");
input.set_fee(1000);
input.set_value(42042042);
input.set_validity_start_height(314159);
input.set_private_key(privateKey.data(), privateKey.size());
input.set_network_id(Proto::NetworkId::MainnetAlbatross);

Proto::SigningOutput output;
ANY_SIGN(input, TWCoinTypeNimiq);

EXPECT_EQ(hex(output.encoded()), "000070c7492aaa9c9ac7a05bc0d9c5db2dae9372029654f71f0c7f95deed5099b7021450ffc385cd4e7c6ac9a7e91614ca67ff90568a00000000028182ba00000000000003e80004cb2f180ba678744be3bf9cd44fbcdabfb5be209f21739934836e26055610ab02720fa99489219d9f3581664473a1b40b30ad1f6e13150d59f8234a42c3f0de3d505405");
}

} // namespace TW::Nimiq::tests
6 changes: 4 additions & 2 deletions tests/chains/Nimiq/TransactionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ TEST(NimiqTransaction, PreImage) {
Address("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA"),
42042042,
1000,
314159
314159,
Proto::NetworkId::UseDefault
);
ASSERT_EQ(hex(tx.getPreImage()),
"000082d5f776378ccbe34a3d941f22d4715bc9f81e0d001450ffc385cd4e7c6ac9a7e91614ca67ff90568a0000000000028182ba00000000000003e80004cb2f2a00");
Expand All @@ -39,7 +40,8 @@ TEST(NimiqTransaction, Serialize) {
Address("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA"),
42042042,
1000,
314159
314159,
Proto::NetworkId::Mainnet
);

const auto signature = parse_hex("74dc7f6e0ab58a0bf52cc6e8801b0cca132dd4229d9a3e3a3d2f90e4d8f045d981b771bf5fc3851a98f3c617b1a943228f963e910e061808a721cfa0e3cad50b");
Expand Down

0 comments on commit 45124dd

Please sign in to comment.