Skip to content

Commit

Permalink
rooch framework release compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
pause125 committed Nov 29, 2023
1 parent 492071d commit c5f1cca
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 0 deletions.
12 changes: 12 additions & 0 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [
"crates/rooch-types",
"crates/rooch-framework",
"crates/rooch-framework-tests",
"crates/rooch-framework-release",
"crates/rooch-genesis",
"crates/rooch-genesis-builder",
"crates/rooch-integration-test-runner",
Expand Down
23 changes: 23 additions & 0 deletions crates/rooch-framework-release/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "rooch-framework-release"
version = "0.1.0"

# Workspace inherited keys
authors = { workspace = true }
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
publish = { workspace = true }
repository = { workspace = true }
rust-version = { workspace = true }

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { features = [ "derive", ], workspace = true }
itertools = { workspace = true }

move-binary-format = { workspace = true }
moveos-stdlib-builder = { workspace = true }
rooch-genesis-builder = { workspace = true }
rooch-types = { workspace = true }
25 changes: 25 additions & 0 deletions crates/rooch-framework-release/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Rooch standard library

Rooch stdlib include the MoveOS standard library and Rooch framework.

This crate is used to compile the moveos-stdlib and rooch-framework, and then generate the compiled Move bytecode, documentation, and error descriptions for use by the Move Explain tool.

## Compile and save the latest stdlib

1. Compile

```bash
cargo run --package rooch-framework-release --bin rooch-framework-release
```

This command will compile the moveos-stdlib and rooch-framework, and then check the compatibility with previous one (if exists), and finally save the new compiled stdlib.

## Release a new version

1. Compile with given version number

```bash
cargo run --package rooch-framework-release --bin rooch-framework-release -- --version 5
```

All modified source files and generated files should be committed.
Binary file not shown.
191 changes: 191 additions & 0 deletions crates/rooch-framework-release/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Copyright (c) RoochNetwork
// SPDX-License-Identifier: Apache-2.0

use clap::Parser;
use itertools::Itertools;
use move_binary_format::{
compatibility::Compatibility, errors::PartialVMResult, normalized::Module, CompiledModule,
};
use moveos_stdlib_builder::Stdlib;
use rooch_genesis_builder::build_stdlib;
use rooch_types::stdlib_version::StdlibVersion;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;

#[derive(Parser)]
#[clap(
name = "rooch-framework-release",
author = "The Rooch Core Contributors"
)]
struct StdlibOpts {
/// print debug log
#[clap(long)]
debug: bool,

/// Version number for compiled stdlib, starting from 1.
#[clap(short = 'v', long, value_name = "VERSION")]
version: Option<u64>,

/// don't check compatibility between the old and new standard library
#[clap(short = 'n', long)]
no_check_compatibility: bool,
}

fn main() {
let opts: StdlibOpts = StdlibOpts::parse();

let curr_stdlib = build_stdlib().unwrap();
let version = StdlibVersion::new(opts.version.unwrap_or(0));
let pre_version = if let Some(version_num) = opts.version {
if version_num > 1 {
Some(StdlibVersion::new(version_num - 1))
} else {
None
}
} else {
// Read dirname in compiled dir, to get the max version number
let max_version = current_max_version();
if max_version > 0 {
Some(StdlibVersion::new(max_version))
} else {
None
}
};

// check compatibility
if let Some(pre_version) = pre_version {
if !opts.no_check_compatibility {
let prev_stdlib = Stdlib::load_from_file(stdlib_output_file(&pre_version.as_string()))
.expect(&format!(
"load previous stdlib (version {:}) failed",
pre_version.as_string()
));
assert_stdlib_compatibility(&curr_stdlib, &prev_stdlib);
}
}
curr_stdlib
.save_to_file(stdlib_output_file(&version.as_string()))
.unwrap();
}

/// Check whether the new stdlib is compatible with the old stdlib
fn assert_stdlib_compatibility(curr_stdlib: &Stdlib, prev_stdlib: &Stdlib) {
let new_modules = curr_stdlib
.all_modules()
.expect("Extract modules from new stdlib failed");
let old_modules = prev_stdlib
.all_modules()
.expect("Extract modules from old stdlib failed");
let new_modules_map = new_modules
.into_iter()
.map(|module| (module.self_id(), module))
.collect::<HashMap<_, _>>();
let old_modules_map = old_modules
.into_iter()
.map(|module| (module.self_id(), module))
.collect::<HashMap<_, _>>();

let incompatible_module_ids = new_modules_map
.values()
.into_iter()
.filter_map(|module| {
let module_id = module.self_id();
if let Some(old_module) = old_modules_map.get(&module_id) {
let compatibility = check_compiled_module_compat(old_module, module);
if compatibility.is_err() {
Some(module_id)
} else {
None
}
} else {
println!("Module {:?} is new module.", module_id);
None
}
})
.collect::<Vec<_>>();

if !incompatible_module_ids.is_empty() {
eprintln!(
"Modules {} is incompatible with previous version!",
incompatible_module_ids
.into_iter()
.map(|module_id| module_id.to_string())
.join(","),
);
std::process::exit(1);
}

let deleted_module_ids = old_modules_map
.keys()
.into_iter()
.filter_map(|module_id| {
if !new_modules_map.contains_key(module_id) {
Some(module_id.clone())
} else {
None
}
})
.collect::<Vec<_>>();

if !deleted_module_ids.is_empty() {
eprintln!(
"Modules {} is deleted!",
deleted_module_ids
.into_iter()
.map(|module_id| module_id.to_string())
.join(","),
);
std::process::exit(1);
}
}

/// check module compatibility
fn check_compiled_module_compat(
new_module: &CompiledModule,
old_module: &CompiledModule,
) -> PartialVMResult<()> {
let new_m = Module::new(new_module);
let old_m = Module::new(old_module);
// TODO: config compatibility through global configuration
let compat = Compatibility::full_check();
compat.check(&old_m, &new_m)
}

/// Read max version number except `latest` from stdlib release dir
fn current_max_version() -> u64 {
let mut max_version = 0;
for entry in release_dir().read_dir().unwrap() {
let entry = entry.unwrap();
let dirname = entry.file_name();
if let Some(dirname_str) = dirname.to_str() {
if let Ok(version) = dirname_str.parse::<u64>() {
if version > max_version {
max_version = version;
}
}
}
}
max_version
}

fn stdlib_output_file(version_str: &str) -> PathBuf {
let version_dir = release_dir().join(version_str);
if !version_dir.exists() {
std::fs::create_dir(&version_dir).expect(&format!("Create dir {:?} failed", version_dir));
}
version_dir.join("stdlib")
}

fn release_dir() -> PathBuf {
path_in_crate(format!("../rooch-framework-release/compiled/",))
}

fn path_in_crate<S>(relative: S) -> PathBuf
where
S: AsRef<Path>,
{
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push(relative);
path
}
1 change: 1 addition & 0 deletions crates/rooch-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ pub mod into_address;
pub mod key_struct;
pub mod multichain_id;
pub mod sequencer;
pub mod stdlib_version;
pub mod transaction;
95 changes: 95 additions & 0 deletions crates/rooch-types/src/stdlib_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) RoochNetwork
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::Formatter;
use std::fmt::{Debug, Display};
use std::str::FromStr;

#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum StdlibVersion {
Latest,
Version(VersionNumber),
}

type VersionNumber = u64;

impl StdlibVersion {
pub fn new(version: u64) -> Self {
if version == 0 {
StdlibVersion::Latest
} else {
StdlibVersion::Version(version)
}
}

pub fn as_string(&self) -> String {
match self {
StdlibVersion::Latest => "latest".to_string(),
StdlibVersion::Version(version) => format!("{}", version),
}
}

pub fn version(&self) -> u64 {
match self {
StdlibVersion::Latest => 0,
StdlibVersion::Version(version) => *version,
}
}

pub fn is_latest(&self) -> bool {
matches!(self, StdlibVersion::Latest)
}

/// If `version`` is compatible with previous version
pub fn compatible_with_previous(_version: &StdlibVersion) -> bool {
true
}
}

impl PartialOrd for StdlibVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for StdlibVersion {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(StdlibVersion::Latest, StdlibVersion::Latest) => Ordering::Equal,
(StdlibVersion::Latest, _) => Ordering::Greater,
(_, StdlibVersion::Latest) => Ordering::Less,
(StdlibVersion::Version(self_v), StdlibVersion::Version(other_v)) => {
self_v.cmp(other_v)
}
}
}
}

impl Default for StdlibVersion {
fn default() -> Self {
StdlibVersion::Latest
}
}

impl FromStr for StdlibVersion {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"latest" => Ok(StdlibVersion::Latest),
s => Ok(Self::new(s.parse()?)),
}
}
}

impl Display for StdlibVersion {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
StdlibVersion::Latest => f.write_str("latest"),
StdlibVersion::Version(version) => f.write_str(version.to_string().as_str()),
}
}
}

0 comments on commit c5f1cca

Please sign in to comment.