From fc9afa8368ab695dd0db10f7ae19a3dca1d1d7b5 Mon Sep 17 00:00:00 2001 From: Teng Zhang Date: Tue, 19 Nov 2024 06:59:12 +0000 Subject: [PATCH] [CLI] add option to `aptos move decompile` and `aptos move disassemble` to print out metadata attached to the bytecode (#15273) * add compiler option * print metadata * handle comments * print json * update changelog --- crates/aptos/CHANGELOG.md | 1 + crates/aptos/src/move_tool/bytecode.rs | 62 ++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/crates/aptos/CHANGELOG.md b/crates/aptos/CHANGELOG.md index a0efff726a5d8..d3d84666039ae 100644 --- a/crates/aptos/CHANGELOG.md +++ b/crates/aptos/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to the Aptos CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased +- Add option `--print-metadata-only` to `aptos move decompile` and `aptos move disassemble` to print out the metadata attached to the bytecode. ## [4.5.0] - 2024/11/15 - Determine network from URL to make explorer links better for legacy users diff --git a/crates/aptos/src/move_tool/bytecode.rs b/crates/aptos/src/move_tool/bytecode.rs index 6a662300b20db..fdb77c9d89571 100644 --- a/crates/aptos/src/move_tool/bytecode.rs +++ b/crates/aptos/src/move_tool/bytecode.rs @@ -12,6 +12,10 @@ use crate::{ update::get_revela_path, }; use anyhow::Context; +use aptos_framework::{ + get_compilation_metadata_from_compiled_module, get_compilation_metadata_from_compiled_script, + get_metadata_from_compiled_module, get_metadata_from_compiled_script, RuntimeModuleMetadataV1, +}; use async_trait::async_trait; use clap::{Args, Parser}; use itertools::Itertools; @@ -25,10 +29,13 @@ use move_command_line_common::files::{ use move_coverage::coverage_map::CoverageMap; use move_disassembler::disassembler::{Disassembler, DisassemblerOptions}; use move_ir_types::location::Spanned; +use move_model::metadata::{CompilationMetadata, CompilerVersion, LanguageVersion}; +use serde::{Deserialize, Serialize}; use std::{ fs, path::{Path, PathBuf}, process::Command, + str, }; const DISASSEMBLER_EXTENSION: &str = "mv.asm"; @@ -80,6 +87,11 @@ pub struct BytecodeCommand { #[clap(flatten)] pub(crate) prompt_options: PromptOptions, + + /// When `--bytecode-path` is set with this option, + /// only print out the metadata and bytecode version of the target bytecode + #[clap(long)] + pub print_metadata_only: bool, } /// Allows to ensure that either one of both is selected (via the `group` attribute). @@ -127,6 +139,13 @@ impl CliCommand for Decompile { } } +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +struct BytecodeMetadata { + aptos_metadata: Option, + bytecode_version: u32, + compilation_metadata: CompilationMetadata, +} + impl BytecodeCommand { async fn execute(self, command_type: BytecodeCommandType) -> CliTypedResult { let inputs = if let Some(path) = self.input.bytecode_path.clone() { @@ -141,6 +160,10 @@ impl BytecodeCommand { unreachable!("arguments required by clap") }; + if self.print_metadata_only && self.input.bytecode_path.is_some() { + return self.print_metadata(&inputs[0]); + } + let mut report = vec![]; let mut last_out_dir = String::new(); for bytecode_path in inputs { @@ -201,6 +224,45 @@ impl BytecodeCommand { }) } + fn print_metadata(&self, bytecode_path: &Path) -> Result { + let bytecode_bytes = read_from_file(bytecode_path)?; + + let v1_metadata = CompilationMetadata { + unstable: false, + compiler_version: CompilerVersion::V1.to_string(), + language_version: LanguageVersion::V1.to_string(), + }; + let metadata = if self.is_script { + let script = CompiledScript::deserialize(&bytecode_bytes) + .context("Script blob can't be deserialized")?; + if let Some(data) = get_compilation_metadata_from_compiled_script(&script) { + serde_json::to_string_pretty(&data).expect("expect compilation metadata") + } else { + serde_json::to_string_pretty(&v1_metadata).expect("expect compilation metadata") + }; + BytecodeMetadata { + aptos_metadata: get_metadata_from_compiled_script(&script), + bytecode_version: script.version, + compilation_metadata: get_compilation_metadata_from_compiled_script(&script) + .unwrap_or(v1_metadata), + } + } else { + let module = CompiledModule::deserialize(&bytecode_bytes) + .context("Module blob can't be deserialized")?; + BytecodeMetadata { + aptos_metadata: get_metadata_from_compiled_module(&module), + bytecode_version: module.version, + compilation_metadata: get_compilation_metadata_from_compiled_module(&module) + .unwrap_or(v1_metadata), + } + }; + println!( + "Metadata: {}", + serde_json::to_string_pretty(&metadata).expect("expect metadata") + ); + Ok("ok".to_string()) + } + fn disassemble(&self, bytecode_path: &Path) -> Result { let bytecode_bytes = read_from_file(bytecode_path)?; let move_path = bytecode_path.with_extension(MOVE_EXTENSION);