From 69e36c1443497c615979972e7619229d77d6891c Mon Sep 17 00:00:00 2001
From: librelois <c@elo.tf>
Date: Wed, 25 May 2022 15:50:56 +0200
Subject: [PATCH] feat(xtask): add subcommand inject-runtime-code

---
 Cargo.lock        |  3 ++
 xtask/Cargo.toml  |  3 ++
 xtask/src/main.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/Cargo.lock b/Cargo.lock
index ef1ee7c1b..01a4067c6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9000,9 +9000,12 @@ dependencies = [
  "anyhow",
  "clap",
  "frame-metadata",
+ "hex",
+ "memmap2 0.5.0",
  "parity-scale-codec",
  "run_script",
  "scale-info",
+ "serde_json",
  "version-compare",
  "version_check",
 ]
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 43fefae3b..c9f4dc22c 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -17,7 +17,10 @@ anyhow = "1.0.32"
 clap = { version = "3.0", features = ["derive"] }
 codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive", "full", "bit-vec"] }
 frame-metadata = "14.0.0"
+hex = "0.4"
+memmap2 = "0.5.0"
 run_script = "0.6.3"
 scale-info = { version = "1.0.0", features = ["bit-vec"] }
+serde_json = "1.0"
 version_check = "0.9.2"
 version-compare = "0.0.11"
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 0873750f8..ee06e50af 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -16,8 +16,10 @@
 
 mod gen_calls_doc;
 
-use anyhow::Result;
+use anyhow::{Context, Result};
 use clap::Parser;
+use std::io::{BufReader, BufWriter, Read, Write};
+use std::path::{Path, PathBuf};
 use std::process::Command;
 
 const MIN_RUST_VERSION: &str = "1.58.0";
@@ -37,6 +39,15 @@ enum DuniterXTaskCommand {
     },
     /// Generate calls documentation
     GenCallsDoc,
+    /// Inject runtime code in raw specs
+    InjectRuntimeCode {
+        #[clap(short, long, parse(from_os_str))]
+        /// Runtime filepath
+        runtime: PathBuf,
+        #[clap(short = 's', long, parse(from_os_str))]
+        /// Raw spec filepath
+        raw_spec: PathBuf,
+    },
     /// Execute unit tests and integration tests
     /// End2tests are skipped
     Test,
@@ -60,10 +71,67 @@ fn main() -> Result<()> {
     match args.command {
         DuniterXTaskCommand::Build { production } => build(production),
         DuniterXTaskCommand::GenCallsDoc => gen_calls_doc::gen_calls_doc(),
+        DuniterXTaskCommand::InjectRuntimeCode { runtime, raw_spec } => {
+            inject_runtime_code(&raw_spec, &runtime)
+        }
         DuniterXTaskCommand::Test => test(),
     }
 }
 
+fn inject_runtime_code(raw_spec: &Path, runtime: &Path) -> Result<()> {
+    // Read runtime code
+    // SAFETY: `mmap` is fundamentally unsafe since technically the file can change
+    //         underneath us while it is mapped; in practice it's unlikely to be a problem
+    let mut file =
+        std::fs::File::open(runtime).with_context(|| "Failed to open runtime wasm file")?;
+    let mut runtime_code =
+        unsafe { memmap2::Mmap::map(&file).with_context(|| "Failed to read runtime wasm file")? };
+
+    // Read raw spec
+    let mut file = std::fs::File::open(raw_spec).with_context(|| "Failed to open raw spec file")?;
+    let reader = BufReader::new(file);
+    let mut json: serde_json::Value =
+        serde_json::from_reader(reader).with_context(|| "Failed to read raw spec file")?;
+    println!("json raw specs loaded!");
+
+    let mut hex_runtime_code = String::with_capacity(2 + (runtime_code.len() * 2));
+    hex_runtime_code.push('0');
+    hex_runtime_code.push('x');
+    hex_runtime_code.push_str(&hex::encode(runtime_code));
+    //hex::encode_to_slice(runtime_code, &mut hex_runtime_code[2..])
+    //.with_context(|| "fail to convert runtime code to hex")?;
+
+    const CODE_KEY: &str = "0x3a636f6465";
+
+    json.as_object_mut()
+        .with_context(|| "invalid raw spec file")?
+        .get_mut("genesis")
+        .with_context(|| "invalid raw spec file: missing field genesis")?
+        .as_object_mut()
+        .with_context(|| "invalid raw spec file")?
+        .get_mut("raw")
+        .with_context(|| "invalid raw spec file: missing field raw")?
+        .as_object_mut()
+        .with_context(|| "invalid raw spec file")?
+        .get_mut("top")
+        .with_context(|| "invalid raw spec file: missing field top")?
+        .as_object_mut()
+        .with_context(|| "invalid raw spec file")?
+        .insert(
+            CODE_KEY.to_owned(),
+            serde_json::Value::String(unsafe { std::mem::transmute(hex_runtime_code) }),
+        );
+
+    // Write modified raw specs
+
+    let mut file = std::fs::File::create(raw_spec)?;
+
+    serde_json::to_writer_pretty(BufWriter::new(file), &json)
+        .with_context(|| "fail to write raw specs")?;
+
+    Ok(())
+}
+
 fn build(_production: bool) -> Result<()> {
     exec_should_success(Command::new("cargo").args(&["clean", "-p", "duniter"]))?;
     exec_should_success(Command::new("cargo").args(&["build", "--locked"]))?;
-- 
GitLab