Skip to content
This repository has been archived by the owner on Aug 24, 2023. It is now read-only.

Commit

Permalink
FFI support for ain integration
Browse files Browse the repository at this point in the history
* Parse args and run in a separate thread

* Native helper struct for static FFI invocation

* Wait until node starts when run as daemon

* Flags for success, daemon and displaying help
  • Loading branch information
wafflespeanut committed Nov 24, 2022
1 parent 6251c1b commit b5903d2
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ target/

# These are backup files generated by rustfmt
**/*.rs.bk
# Rust package files
pkg/
# ain depends integration artifacts
.stamp_*
master-*

### Substrate
.local
Expand Down
76 changes: 73 additions & 3 deletions Cargo.lock

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

9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CARGO ?= cargo
TARGET ?=

build-native-pkg:
CRATE_CC_NO_DEFAULTS=1 $(CARGO) build --package meta-node --release $(if $(TARGET),--target $(TARGET),)
mkdir -p pkg/metachain/include pkg/metachain/lib
cp target/$(if $(TARGET),$(TARGET)/,)release/libmeta_node.a pkg/metachain/lib/libmetachain.a
cp target/libmc.hpp pkg/metachain/include/
cp target/libmc.cpp pkg/metachain/
13 changes: 12 additions & 1 deletion meta/meta-node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@ version = "0.1.0"
edition = "2021"
build = "build.rs"

[lib]
crate-type = ["staticlib"]

[[bin]]
name = "meta-node"
path = "src/main.rs"

[dependencies]
async-trait = "0.1"
clap = { version = "3.1", features = ["derive"] }
cxx = "1.0"
futures = "0.3.21"
lazy_static = "1.4.0"
log = "0.4.17"
serde_json = "1.0"
tokio = { version = "1.22", features = ["sync"] }
# Parity
codec = { default-features = false, version = "3.1.5", features = ["derive"], package = "parity-scale-codec" }
jsonrpsee = { default-features = false, version = "0.14.0", features = ["server"] }
Expand Down Expand Up @@ -58,4 +68,5 @@ meta-runtime = { package = "meta-runtime", path = "../meta-runtime" }

[build-dependencies]
substrate-build-script-utils = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.25" }

cxx-gen = "0.7"
proc-macro2 = "1.0"
32 changes: 32 additions & 0 deletions meta/meta-node/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
use proc_macro2::TokenStream;
use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};

use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;

fn main() {
generate_cargo_keys();

rerun_if_git_head_changed();

let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let parent = root.clone();
root.pop();
root.pop();
let lib_path = &parent.join("src").join("lib.rs");
println!("cargo:rerun-if-changed={}", lib_path.display());
let target_dir = &root.join("target");

let mut content = String::new();
File::open(lib_path)
.unwrap()
.read_to_string(&mut content)
.unwrap();
let tt: TokenStream = content.parse().unwrap();
let codegen = cxx_gen::generate_header_and_cc(tt, &cxx_gen::Opt::default()).unwrap();

let cpp_stuff = String::from_utf8(codegen.implementation).unwrap();
File::create(target_dir.join("libmc.hpp"))
.unwrap()
.write_all(&codegen.header)
.unwrap();
File::create(target_dir.join("libmc.cpp"))
.unwrap()
.write_all(cpp_stuff.as_bytes())
.unwrap();
}
46 changes: 36 additions & 10 deletions meta/meta-node/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use crate::{
cli::{Cli, Subcommand},
service::{self, db_config_dir},
};
use clap::Parser;
use fc_db::frontier_database_dir;
use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli};
use sc_service::{DatabaseSource, PartialComponents};
use tokio::sync::mpsc;

use std::thread::{self, JoinHandle};

impl SubstrateCli for Cli {
fn impl_name() -> String {
Expand Down Expand Up @@ -48,11 +50,9 @@ impl SubstrateCli for Cli {
}
}

/// Parse and run command line arguments
pub fn run() -> sc_cli::Result<()> {
let cli = Cli::parse();

match &cli.subcommand {
/// Parse and run command line arguments in a separate thread and return handle
pub fn run(cli: Cli) -> sc_cli::Result<Option<JoinHandle<sc_cli::Result<()>>>> {
let res = match &cli.subcommand {
Some(Subcommand::Key(cmd)) => cmd.run(&cli),
Some(Subcommand::BuildSpec(cmd)) => {
let runner = cli.create_runner(cmd)?;
Expand Down Expand Up @@ -151,10 +151,36 @@ pub fn run() -> sc_cli::Result<()> {
})
}
None => {
// Metachain should block until the network starts before handing over
// control to the native chain. One way to accomplish this is by moving
// everything over to a separate thread, wait for the result of node start,
// and let native chain take control after.
let (tx, mut rx) = mpsc::channel(1);
let runner = cli.create_runner(&cli.run.base)?;
runner.run_node_until_exit(|config| async move {
service::new_full(config, &cli).map_err(sc_cli::Error::Service)
})
let handle = thread::spawn(move || {
runner.run_node_until_exit(|config| async move {
match service::new_full(config, &cli) {
Ok((starter, manager)) => {
starter.start_network();
let _ = tx.send(None).await;
Ok(manager)
},
Err(e) => {
let _ = tx.send(Some(e.to_string())).await;
Err(sc_cli::Error::Service(e))
},
}
})
});

// Startup failure, if any
if let Some(Some(msg)) = rx.blocking_recv() {
return Err(msg.into());
}

return Ok(Some(handle));
}
}
};

res.map(|_| None)
}
87 changes: 87 additions & 0 deletions meta/meta-node/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
mod chain_spec;
mod native;
#[macro_use]
mod service;
mod cli;
mod command;
mod rpc;

use std::error::Error;

use clap::{CommandFactory, ErrorKind, FromArgMatches};
use cli::Cli;
use cxx::{CxxString, CxxVector};

// NOTE: Struct definitions for FFI should match those defined in libain-rs/protobuf

#[cxx::bridge]
mod ffi {
pub struct DmcTx {
pub from: String,
pub to: String,
pub amount: i64,
}

pub struct DmcBlock {
pub payload: Vec<u8>,
}

struct ExecResult {
is_help: bool,
success: bool,
daemon: bool,
}

extern "Rust" {
fn mint_block(dmc_txs: &CxxVector<DmcTx>) -> Result<DmcBlock>;
fn parse_args_and_run(args: &CxxVector<CxxString>) -> ExecResult;
}
}

#[inline]
fn mint_block(dmc_txs: &CxxVector<ffi::DmcTx>) -> Result<ffi::DmcBlock, Box<dyn Error>> {
self::native::NATIVE_HELPER
.read().unwrap().as_ref().expect("uninitialized native helper")
.mint_block(dmc_txs.iter())
}

fn parse_args_and_run(args: &CxxVector<CxxString>) -> ffi::ExecResult {
let args: Vec<String> = args
.iter()
.map(|s| s.to_string_lossy().into_owned())
.collect();
let res = Cli::command().try_get_matches_from(args)
.and_then(|mut m| Cli::from_arg_matches_mut(&mut m));

let cli = match res {
Ok(c) => c,
Err(e) => {
let _ = e.print();
let is_help = match e.kind() {
ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => true,
_ => false,
};
return ffi::ExecResult {
is_help,
success: is_help,
daemon: false,
}
},
};

let mut result = ffi::ExecResult {
is_help: false,
success: false,
daemon: cli.subcommand.is_none(),
};
match command::run(cli) {
Ok(_) => {
// Deciding to run the node, give control back to defid without joining handle
// FIXME: Figure out how to send SIGINT on defid exiting for cleanly shutting down
result.success = true;
},
Err(e) => eprintln!("{:?}", e),
}

result
}
Loading

0 comments on commit b5903d2

Please sign in to comment.