Skip to content

Commit

Permalink
[Rust]: Extend codegen-v2 capabilities (#3570)
Browse files Browse the repository at this point in the history
* [Rust]: Small refactoring

* [Rust]: Add `TemplateGenerator`

* Add new blockchain crate templates

* [Rust]: Add new blockchain integration tests templates

* [Rust]: Add new blockchain to test_coin_address_derivation

* [C++]: Add cpp::new_blockchain

* Add C++ Entry.h template

* [C++]: Fix cpp::new_blockchain

* [C++]: Add TWCoinType, TWBlockchain enum generating

* [C++]: Add Blockchain entry to the `Coin.cpp` dispatcher

* [Codegen]: Some improvements

* [Codegen]: Revert coin_skeleton_gen.rb

* [Codegen]: Generate Blockchain.proto file

* [Codegen]: Generate TWCoinTypeTests.cppp file

* [Codegen]: Generate TWAnySignerTests.cpp, TWAnyAddressTests.cpp files

* [Codegen]: Fix Protobuf

* [Codegen]: Refactor codegen-v1 to allow to generate mobile tests only

* [Codegen]: Generate CoinAddressDerivationTests.cpp cases

* [Codegen]: Add new-evmchain cmd

* [Codegen]: Refactor C++ code generators

* [Codegen]: Refactor Rust generators, fix sorting error

* [Codegen]: Run codegen-v2 new-blockchain-rust iotex on Rust CI pipeline

* [CI] Trigger CI
  • Loading branch information
satoshiotomakan authored Nov 24, 2023
1 parent b8beaec commit 6ef3eb5
Show file tree
Hide file tree
Showing 59 changed files with 1,664 additions and 360 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/linux-ci-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,18 @@ jobs:
- name: Gather and check Rust code coverage
run: |
tools/check-coverage rust/coverage.stats rust/coverage.info
# Generate files for a blockchain in the end of the pipeline.
# Please note the blockchain should not be implemented in Rust at the moment of running this step,
# otherwise consider either generate files for another blockchain or remove this step at all.
- name: Test codegen-v2 new-blockchain-rust
run: |
cargo run -- new-blockchain-rust iotex
working-directory: codegen-v2

# Check if `new-blockchain-rust` command has generated files that do not break project compilation.
- name: Check Rust compiles
run: |
cargo check --tests
working-directory: rust

10 changes: 10 additions & 0 deletions codegen-v2/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions codegen-v2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ name = "parser"
path = "src/main.rs"

[dependencies]
aho-corasick = "1.1.2"
convert_case = "0.6.0"
pathdiff = "0.2.1"
serde = { version = "1.0.159", features = ["derive"] }
Expand Down
75 changes: 75 additions & 0 deletions codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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 crate::codegen::cpp::cpp_source_directory;
use crate::registry::CoinItem;
use crate::utils::FileContent;
use crate::Result;
use std::path::PathBuf;

const COIN_INCLUDES_END: &str = "end_of_coin_includes_marker_do_not_modify";
const COIN_DISPATCHER_DECLARATIONS_END: &str =
"end_of_coin_dipatcher_declarations_marker_do_not_modify";
const COIN_DISPATCHER_SWITCH_END: &str = "end_of_coin_dipatcher_switch_marker_do_not_modify";

fn dispatcher_coin_cpp_path() -> PathBuf {
cpp_source_directory().join("Coin.cpp")
}

/// Represents `Coin.cpp`.
pub struct BlockchainDispatcherGenerator;

impl BlockchainDispatcherGenerator {
pub fn generate_new_blockchain_type_dispatching(coin: &CoinItem) -> Result<()> {
let mut file_content = FileContent::read(dispatcher_coin_cpp_path())?;

Self::generate_include_of_blockchain_entry(coin, &mut file_content)?;
Self::generate_blockchain_entry_constant(coin, &mut file_content)?;
Self::generate_blockchain_dispatcher_case(coin, &mut file_content)?;

file_content.write()
}

fn generate_include_of_blockchain_entry(
coin: &CoinItem,
file_content: &mut FileContent,
) -> Result<()> {
let blockchain_type = coin.blockchain_type();

let mut line_marker = file_content.rfind_line(|line| line.contains(COIN_INCLUDES_END))?;
line_marker.push_line_before(format!(r#"#include "{blockchain_type}/Entry.h""#));

Ok(())
}

fn generate_blockchain_entry_constant(
coin: &CoinItem,
file_content: &mut FileContent,
) -> Result<()> {
let blockchain_type = coin.blockchain_type();

let mut entries_region =
file_content.rfind_line(|line| line.contains(COIN_DISPATCHER_DECLARATIONS_END))?;
entries_region.push_line_before(format!("{blockchain_type}::Entry {blockchain_type}DP;"));

Ok(())
}

fn generate_blockchain_dispatcher_case(
coin: &CoinItem,
file_content: &mut FileContent,
) -> Result<()> {
let blockchain_type = coin.blockchain_type();

let mut entries_region =
file_content.rfind_line(|line| line.contains(COIN_DISPATCHER_SWITCH_END))?;
entries_region.push_line_before(format!(
" case TWBlockchain{blockchain_type}: entry = &{blockchain_type}DP; break;"
));

Ok(())
}
}
43 changes: 43 additions & 0 deletions codegen-v2/src/codegen/cpp/entry_generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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 crate::codegen::cpp::cpp_source_directory;
use crate::codegen::template_generator::TemplateGenerator;
use crate::registry::CoinItem;
use crate::{Error, Result};
use std::path::PathBuf;
use std::{fs, io};

const ENTRY_HEADER_TEMPLATE: &str = include_str!("templates/Entry.h");

pub fn coin_source_directory(coin: &CoinItem) -> PathBuf {
cpp_source_directory().join(coin.blockchain_type())
}

pub struct EntryGenerator;

impl EntryGenerator {
pub fn generate(coin: &CoinItem) -> Result<PathBuf> {
let blockchain_dir = coin_source_directory(coin);
let entry_header_path = blockchain_dir.join("Entry.h");

if blockchain_dir.exists() {
return Err(Error::IoError(io::Error::new(
io::ErrorKind::AlreadyExists,
"blockchain already exists",
)));
}

fs::create_dir(&blockchain_dir)?;

TemplateGenerator::new(ENTRY_HEADER_TEMPLATE)
.write_to(entry_header_path.clone())
.with_default_patterns(coin)
.write()?;

Ok(entry_header_path)
}
}
45 changes: 45 additions & 0 deletions codegen-v2/src/codegen/cpp/mod.rs
Original file line number Diff line number Diff line change
@@ -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.

use crate::registry::CoinItem;
use std::env;
use std::path::PathBuf;

pub mod blockchain_dispatcher_generator;
pub mod entry_generator;
pub mod new_blockchain;
pub mod new_evmchain;
pub mod tw_any_address_tests_generator;
pub mod tw_any_signer_tests_generator;
pub mod tw_blockchain;
pub mod tw_coin_address_derivation_tests_generator;
pub mod tw_coin_type_generator;
pub mod tw_coin_type_tests_generator;

pub fn cpp_source_directory() -> PathBuf {
PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
.join("..")
.join("src")
}

pub fn cpp_include_directory() -> PathBuf {
PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
.join("..")
.join("include")
.join("TrustWalletCore")
}

pub fn integration_tests_directory() -> PathBuf {
PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
.join("..")
.join("tests")
}

pub fn coin_integration_tests_directory(coin: &CoinItem) -> PathBuf {
integration_tests_directory()
.join("chains")
.join(coin.coin_type())
}
36 changes: 36 additions & 0 deletions codegen-v2/src/codegen/cpp/new_blockchain.rs
Original file line number Diff line number Diff line change
@@ -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.

use crate::codegen::cpp::blockchain_dispatcher_generator::BlockchainDispatcherGenerator;
use crate::codegen::cpp::entry_generator::EntryGenerator;
use crate::codegen::cpp::tw_any_address_tests_generator::TWAnyAddressTestsGenerator;
use crate::codegen::cpp::tw_any_signer_tests_generator::TWAnySignerTestsGenerator;
use crate::codegen::cpp::tw_blockchain::TWBlockchainGenerator;
use crate::codegen::cpp::tw_coin_address_derivation_tests_generator::CoinAddressDerivationTestsGenerator;
use crate::codegen::cpp::tw_coin_type_generator::TWCoinTypeGenerator;
use crate::codegen::cpp::tw_coin_type_tests_generator::TWCoinTypeTestsGenerator;
use crate::registry::CoinItem;
use crate::Result;

pub fn new_blockchain(coin: &CoinItem) -> Result<()> {
// Generate C++ files.
EntryGenerator::generate(coin)?;

// Add the new coin type to the `TWCoinType` enum.
TWCoinTypeGenerator::generate_coin_type_variant(coin)?;
// Add the new blockchain type to the `TWBlockchain` enum.
TWBlockchainGenerator::generate_blockchain_type_variant(coin)?;
// Add the blockchain entry to the dispatcher `Coin.cpp`.
BlockchainDispatcherGenerator::generate_new_blockchain_type_dispatching(coin)?;

// Add integration tests.
TWCoinTypeTestsGenerator::generate(coin)?;
TWAnyAddressTestsGenerator::generate(coin)?;
TWAnySignerTestsGenerator::generate(coin)?;
CoinAddressDerivationTestsGenerator::generate_new_coin_type_case(coin)?;

Ok(())
}
22 changes: 22 additions & 0 deletions codegen-v2/src/codegen/cpp/new_evmchain.rs
Original file line number Diff line number Diff line change
@@ -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.

use crate::codegen::cpp::tw_coin_address_derivation_tests_generator::CoinAddressDerivationTestsGenerator;
use crate::codegen::cpp::tw_coin_type_generator::TWCoinTypeGenerator;
use crate::codegen::cpp::tw_coin_type_tests_generator::TWCoinTypeTestsGenerator;
use crate::registry::CoinItem;
use crate::Result;

pub fn new_evmchain(coin: &CoinItem) -> Result<()> {
// Add the new coin type to the `TWCoinType` enum.
TWCoinTypeGenerator::generate_coin_type_variant(coin)?;

// Add integration tests.
TWCoinTypeTestsGenerator::generate(coin)?;
CoinAddressDerivationTestsGenerator::generate_new_evm_coin_type_case(coin)?;

Ok(())
}
19 changes: 19 additions & 0 deletions codegen-v2/src/codegen/cpp/templates/Entry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright © 2017-{YEAR} 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 "rust/RustCoinEntry.h"

namespace TW::{BLOCKCHAIN} {

/// Entry point for {BLOCKCHAIN} coin.
/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file
class Entry : public Rust::RustCoinEntry {
};

} // namespace TW::{BLOCKCHAIN}

26 changes: 26 additions & 0 deletions codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright © 2017-{YEAR} 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 <TrustWalletCore/TWAnyAddress.h>
#include "HexCoding.h"

#include "TestUtilities.h"
#include <gtest/gtest.h>

using namespace TW;

// TODO: Finalize tests

TEST(TW{COIN_TYPE}, Address) {
// TODO: Finalize test implementation

auto string = STRING("__ADD_VALID_ADDRESS_HERE__");
auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinType{COIN_TYPE}));
auto string2 = WRAPS(TWAnyAddressDescription(addr.get()));
EXPECT_TRUE(TWStringEqual(string.get(), string2.get()));
auto keyHash = WRAPD(TWAnyAddressData(addr.get()));
assertHexEqual(keyHash, "__CORRESPONDING_ADDRESS_DATA__");
}
19 changes: 19 additions & 0 deletions codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright © 2017-{YEAR} 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 <TrustWalletCore/TWAnySigner.h>
#include "HexCoding.h"

#include "TestUtilities.h"
#include <gtest/gtest.h>

using namespace TW;

// TODO: Finalize tests

TEST(TWAnySigner{COIN_TYPE}, Sign) {
// TODO: Finalize test implementation
}
31 changes: 31 additions & 0 deletions codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright © 2017-{YEAR} 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 <TrustWalletCore/TWCoinTypeConfiguration.h>
#include <gtest/gtest.h>

TEST(TW{COIN_TYPE}CoinType, TWCoinType) {
const auto coin = TWCoinType{COIN_TYPE};
const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin));
const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin));
const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin));
const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("{EXPLORER_SAMPLE_TX}"));
const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get()));
const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("{EXPLORER_SAMPLE_ACCOUNT}"));
const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get()));

assertStringsEqual(id, "{COIN_ID}");
assertStringsEqual(name, "{COIN_TYPE}");
assertStringsEqual(symbol, "{SYMBOL}");
ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), {DECIMALS});
ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchain{BLOCKCHAIN});
ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), {P2PKH_PREFIX});
ASSERT_EQ(TWCoinTypeP2shPrefix(coin), {P2SH_PREFIX});
ASSERT_EQ(TWCoinTypeStaticPrefix(coin), {STATIC_PREFIX});
assertStringsEqual(txUrl, "{EXPLORER_URL}{EXPLORER_TX_PATH}{EXPLORER_SAMPLE_TX}");
assertStringsEqual(accUrl, "{EXPLORER_URL}{EXPLORER_ACCOUNT_PATH}{EXPLORER_SAMPLE_ACCOUNT}");
}
Loading

0 comments on commit 6ef3eb5

Please sign in to comment.