From f1f609bad9aa22639c70967b5b0a5b22ac875378 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 17 May 2024 16:18:02 +0200 Subject: [PATCH 1/4] Add entry codec to prepare entry points Added Input2 format as a temporary measure to work on entry codec. Ultimately we should have a single input.json file with a well defined format. --- external-crates/move/Cargo.lock | 45 ++++++++++++++++ .../move-mv-llvm-compiler/tests/rbpf-tests.rs | 17 +++++++ .../tests/rbpf-tests/basic2/Move.toml | 9 ++++ .../tests/rbpf-tests/basic2/input.json | 4 ++ .../rbpf-tests/basic2/sources/basic.move | 24 +++++++++ .../tests/test_common.rs | 20 ++++++++ .../solana/move-to-solana/src/entry_codec.rs | 51 +++++++++++++++++++ .../move/solana/move-to-solana/src/lib.rs | 1 + .../move/solana/solana-move-cli/Cargo.toml | 49 ++++++++++++++++++ .../move/solana/solana-move-cli/src/main.rs | 49 ++++++++++++++++++ 10 files changed, 269 insertions(+) create mode 100644 external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/Move.toml create mode 100644 external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/input.json create mode 100644 external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/sources/basic.move create mode 100644 external-crates/move/solana/move-to-solana/src/entry_codec.rs create mode 100644 external-crates/move/solana/solana-move-cli/Cargo.toml create mode 100644 external-crates/move/solana/solana-move-cli/src/main.rs diff --git a/external-crates/move/Cargo.lock b/external-crates/move/Cargo.lock index 00572ccd55b52..8b4df57127e29 100644 --- a/external-crates/move/Cargo.lock +++ b/external-crates/move/Cargo.lock @@ -4930,6 +4930,51 @@ dependencies = [ "thiserror", ] +[[package]] +name = "solana-move-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "atty", + "blake3 0.1.5", + "bs58 0.5.0", + "chrono", + "clap 3.2.23", + "codespan", + "codespan-reporting", + "colored", + "env_logger 0.8.4", + "extension-trait", + "itertools", + "libc", + "llvm-sys", + "log", + "move-binary-format", + "move-bytecode-source-map", + "move-command-line-common", + "move-compiler", + "move-core-types", + "move-ir-types", + "move-model", + "move-native", + "move-stackless-bytecode", + "move-symbol-pool", + "move-to-solana", + "num 0.4.0", + "num-traits", + "once_cell", + "parking_lot 0.11.2", + "regex", + "semver", + "serde", + "serde_json", + "solana-bpf-loader-program", + "solana-program-runtime", + "solana-sdk", + "solana_rbpf", + "tempfile", +] + [[package]] name = "solana-program" version = "1.17.0" diff --git a/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests.rs b/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests.rs index 243855ce70c95..5f07135e6578a 100644 --- a/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests.rs +++ b/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests.rs @@ -422,6 +422,12 @@ fn run_rbpf(test_plan: &tc::TestPlan, exe: &Path) -> anyhow::Result<()> { .directives .iter() .find(|&x| matches!(x, tc::TestDirective::Input(_x))); + let input_directive = input_directive.or( + test_plan + .directives + .iter() + .find(|&x| matches!(x, tc::TestDirective::Input2(_x))) + ); if let Some(tc::TestDirective::Input(input)) = input_directive { instruction_data = input.instruction_data.clone(); program_id = input.program_id.parse::().unwrap_or_else(|err| { @@ -459,6 +465,17 @@ fn run_rbpf(test_plan: &tc::TestPlan, exe: &Path) -> anyhow::Result<()> { }); } } + if let Some(tc::TestDirective::Input2(input)) = input_directive { + program_id = input.program_id.parse::().expect("program_id"); + let message = move_to_solana::entry_codec::generate_move_call_message( + program_id.clone(), + &Hash::new_unique(), + None, + &input.entry_fn, + &[], + )?; + instruction_data = message.instructions[0].data.clone(); + } transaction_accounts.push(( loader_id, AccountSharedData::new(0, 0, &solana_sdk::native_loader::id()), diff --git a/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/Move.toml b/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/Move.toml new file mode 100644 index 0000000000000..452404ba2f37a --- /dev/null +++ b/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/Move.toml @@ -0,0 +1,9 @@ +[package] +name = "basic2" +version = "1.0.0" + +[addresses] +basic = "0xba31" + +[dependencies] +MoveStdlib = { local = "../../../../../crates/move-stdlib", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/input.json b/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/input.json new file mode 100644 index 0000000000000..9e78fcc302fd0 --- /dev/null +++ b/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/input.json @@ -0,0 +1,4 @@ +{ + "program_id": "DozgQiYtGbdyniV2T74xMdmjZJvYDzoRFFqw7UR5MwPK", + "entry_fn": "basic__bar" +} diff --git a/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/sources/basic.move b/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/sources/basic.move new file mode 100644 index 0000000000000..6dbcca4f7e166 --- /dev/null +++ b/external-crates/move/solana/move-mv-llvm-compiler/tests/rbpf-tests/basic2/sources/basic.move @@ -0,0 +1,24 @@ +// input2 ../input.json +// log 19 + +module 0x10::debug { + native public fun print(x: &T); +} + +module basic::basic { + use 0x10::debug; + use std::signer; + + public entry fun bar(): u64 { + let rv = 19; + debug::print(&rv); + rv + } + + public entry fun foo(account: &signer): u64 { + let rv = 17; + debug::print(&rv); + debug::print(&signer::address_of(account)); + rv + } +} diff --git a/external-crates/move/solana/move-mv-llvm-compiler/tests/test_common.rs b/external-crates/move/solana/move-mv-llvm-compiler/tests/test_common.rs index 953824ab85617..53f71a5a8b4bb 100644 --- a/external-crates/move/solana/move-mv-llvm-compiler/tests/test_common.rs +++ b/external-crates/move/solana/move-mv-llvm-compiler/tests/test_common.rs @@ -147,6 +147,7 @@ pub enum TestDirective { Abort(u64), // The test should abort. Log(String), // Test should pass. Input(Input), + Input2(Input2), UseStdlib, // Build and link the move stdlib as bytecode } @@ -167,6 +168,12 @@ pub struct Input { pub instruction_data: Vec, } +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct Input2 { + pub program_id: String, + pub entry_fn: String, +} + impl TestPlan { pub fn should_ignore(&self) -> bool { self.directives.contains(&TestDirective::Ignore) @@ -264,6 +271,13 @@ fn load_accounts(path: PathBuf) -> Result { Ok(input) } +fn load_accounts2(path: PathBuf) -> Result { + debug!("Reading input file {path:?}"); + let file = fs::File::open(path).unwrap(); + let input: Input2 = serde_json::from_reader(file)?; + Ok(input) +} + fn load_directives(test_path: &Path) -> anyhow::Result> { let mut directives = Vec::new(); let source = std::fs::read_to_string(test_path)?; @@ -297,6 +311,12 @@ fn load_directives(test_path: &Path) -> anyhow::Result> { let input = load_accounts(filename).unwrap(); directives.push(TestDirective::Input(input)); } + if line.starts_with("input2 ") { + let filename = line.split(' ').nth(1).expect("input file name"); + let filename = test_path.parent().unwrap().join(filename); + let input = load_accounts2(filename).unwrap(); + directives.push(TestDirective::Input2(input)); + } if line.starts_with("signers ") { let s = line.split(' ').nth(1).expect("signer list"); directives.push(TestDirective::Signers(s.to_string())); diff --git a/external-crates/move/solana/move-to-solana/src/entry_codec.rs b/external-crates/move/solana/move-to-solana/src/entry_codec.rs new file mode 100644 index 0000000000000..f853ff7f63823 --- /dev/null +++ b/external-crates/move/solana/move-to-solana/src/entry_codec.rs @@ -0,0 +1,51 @@ +use anyhow::Result; +use solana_sdk::message::Message; +use solana_sdk::hash::Hash; +use solana_sdk::pubkey::Pubkey; +use solana_sdk::instruction::Instruction; +use move_core_types::annotated_value::MoveValue; + +pub fn generate_move_call_message( + program_id: Pubkey, + recent_block_hash: &Hash, + payer: Option<&Pubkey>, + entry_fn_name: &str, + args: &[MoveValue], +) -> Result { + let insn_data = encode_instruction_data( + entry_fn_name, + args, + )?; + + let insn = Instruction::new_with_bytes( + program_id, + &insn_data, + vec![], + ); + + let msg = Message::new_with_blockhash( + &[insn], + payer, + recent_block_hash, + ); + + Ok(msg) +} + +fn encode_instruction_data( + entry_fn_name: &str, + _args: &[MoveValue], +) -> Result> { + let mut insn_data: Vec = vec![]; + + // First encode the entry function name + { + let entry_fn_len: u64 = entry_fn_name.len() as u64; + insn_data.extend(&entry_fn_len.to_le_bytes()); + insn_data.extend(entry_fn_name.as_bytes()); + } + + // todo + + Ok(insn_data) +} diff --git a/external-crates/move/solana/move-to-solana/src/lib.rs b/external-crates/move/solana/move-to-solana/src/lib.rs index be1d98cec744b..5523b072c3ff9 100644 --- a/external-crates/move/solana/move-to-solana/src/lib.rs +++ b/external-crates/move/solana/move-to-solana/src/lib.rs @@ -6,6 +6,7 @@ pub mod cstr; pub mod options; pub mod runner; pub mod stackless; +pub mod entry_codec; use crate::{ options::Options, diff --git a/external-crates/move/solana/solana-move-cli/Cargo.toml b/external-crates/move/solana/solana-move-cli/Cargo.toml new file mode 100644 index 0000000000000..db995cb85fec3 --- /dev/null +++ b/external-crates/move/solana/solana-move-cli/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "solana-move-cli" +version = "0.1.0" +authors = ["Diem Association "] +description = "Generate An Instruction Set code from Move" +license = "Apache-2.0" +publish = false +edition = "2021" + +[dependencies] +anyhow = "1.0.52" +atty = "0.2.14" +blake3 = "0.1.5" +bs58 = "0.5.0" +chrono = "0.4" +clap = { version = "3.1.8", features = ["derive"] } +codespan = "0.11.1" +codespan-reporting = "0.11.1" +colored = "2.0.0" +env_logger = "0.8.3" +extension-trait = "1.0.1" +itertools = "0.10" +libc = "0.2" +llvm-sys = "170.0.1" +log = "0.4.14" +move-binary-format.workspace = true +move-bytecode-source-map.workspace = true +move-command-line-common.workspace = true +move-compiler.workspace = true +move-core-types.workspace = true +move-ir-types.workspace = true +move-model.workspace = true +move-native.workspace = true +move-stackless-bytecode.workspace = true +move-symbol-pool.workspace = true +move-to-solana.workspace = true +num = "0.4.0" +num-traits = "0.2" +once_cell = "1.10" +parking_lot = "0.11" +regex = "1.1.9" +semver = "1.0.13" +serde = { version = "1.0.124", features = ["derive"] } +serde_json = "1.0.64" +solana-bpf-loader-program = { git = "https://github.com/solana-labs/solana", rev = "5d1538013206c1afe6f9d3c8a1a870cb0bfa9dcd" } +solana-program-runtime = { git = "https://github.com/solana-labs/solana", rev = "5d1538013206c1afe6f9d3c8a1a870cb0bfa9dcd" } +solana-sdk = { git = "https://github.com/solana-labs/solana", rev = "5d1538013206c1afe6f9d3c8a1a870cb0bfa9dcd" } +solana_rbpf = "=0.7.1" +tempfile = "3.2" diff --git a/external-crates/move/solana/solana-move-cli/src/main.rs b/external-crates/move/solana/solana-move-cli/src/main.rs new file mode 100644 index 0000000000000..9667345a9ec0b --- /dev/null +++ b/external-crates/move/solana/solana-move-cli/src/main.rs @@ -0,0 +1,49 @@ +use solana_sdk::pubkey::Pubkey; +use anyhow::Result; +use clap::Parser; + +#[derive(clap::Parser)] +struct Args { + #[clap(subcommand)] + cmd: Command, +} + +#[derive(clap::Subcommand)] +enum Command { + Call(CallCommand), +} + +#[derive(clap::Args)] +struct CallCommand { + program_id: String, + payer: String, + function: String, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + match args.cmd { + Command::Call(cmd) => { + cmd.run() + } + } +} + +impl CallCommand { + fn run(&self) -> Result<()> { + let recent_block_hash = { + todo!() + }; + let program_id = self.program_id.parse::()?; + let payer = self.program_id.parse::()?; + let message = move_to_solana::entry_codec::generate_move_call_message( + program_id, + &recent_block_hash, + Some(&payer), + &self.function, + &[], + )?; + } +} + From 415d193d2666fa57ce808136e5074d8aa23a906c Mon Sep 17 00:00:00 2001 From: ksolana <110843012+ksolana@users.noreply.github.com> Date: Mon, 20 May 2024 11:44:21 -0700 Subject: [PATCH 2/4] Encode entry point data in move-cli --- .../move/solana/move-to-solana/src/entry_codec.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/external-crates/move/solana/move-to-solana/src/entry_codec.rs b/external-crates/move/solana/move-to-solana/src/entry_codec.rs index f853ff7f63823..414bf915adbd5 100644 --- a/external-crates/move/solana/move-to-solana/src/entry_codec.rs +++ b/external-crates/move/solana/move-to-solana/src/entry_codec.rs @@ -38,6 +38,8 @@ fn encode_instruction_data( ) -> Result> { let mut insn_data: Vec = vec![]; + // Encode the data in borsch or bincode format. + // First encode the entry function name { let entry_fn_len: u64 = entry_fn_name.len() as u64; @@ -45,6 +47,18 @@ fn encode_instruction_data( insn_data.extend(entry_fn_name.as_bytes()); } + // encode remaining args of the function + if !_args.is_empty() { + let arglen = _args.len() as u64; + insn_data.extend(&arglen.to_le_bytes()); + for a in _args { + // serialize each move value. + // TODO: sui-graphql-rpc/src/types/move_value.rs has more + // advanced serialization methods to convert to json etc. + insn_data.extend(a.simple_serialize().ok_or(None)); + } + + } // todo Ok(insn_data) From 430a4775a55022e7270ae7f310329cf6417379f3 Mon Sep 17 00:00:00 2001 From: ksolana <110843012+ksolana@users.noreply.github.com> Date: Tue, 28 May 2024 10:13:26 -0700 Subject: [PATCH 3/4] Serialize function parameters Note: this is a naive implementation. Testing required to make sure deserialization works --- .../move/solana/move-to-solana/src/entry_codec.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/external-crates/move/solana/move-to-solana/src/entry_codec.rs b/external-crates/move/solana/move-to-solana/src/entry_codec.rs index 414bf915adbd5..5b195ab280e14 100644 --- a/external-crates/move/solana/move-to-solana/src/entry_codec.rs +++ b/external-crates/move/solana/move-to-solana/src/entry_codec.rs @@ -55,9 +55,12 @@ fn encode_instruction_data( // serialize each move value. // TODO: sui-graphql-rpc/src/types/move_value.rs has more // advanced serialization methods to convert to json etc. - insn_data.extend(a.simple_serialize().ok_or(None)); + let argstring = match a.simple_serialize() { + Some(bytes) => bytes, + None => panic!("Error"), + }; + insn_data.extend(std::str::from_utf8(&argstring)?.as_bytes()); } - } // todo From bc5249a1e59d489eceb048b13a1972b73fae59eb Mon Sep 17 00:00:00 2001 From: ksolana <110843012+ksolana@users.noreply.github.com> Date: Tue, 28 May 2024 10:14:39 -0700 Subject: [PATCH 4/4] NFC: Comment on TestDirective and input.json --- .../solana/move-mv-llvm-compiler/tests/test_common.rs | 5 +++-- .../move/solana/move-to-solana/src/entry_codec.rs | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/external-crates/move/solana/move-mv-llvm-compiler/tests/test_common.rs b/external-crates/move/solana/move-mv-llvm-compiler/tests/test_common.rs index 53f71a5a8b4bb..022063c01ce9d 100644 --- a/external-crates/move/solana/move-mv-llvm-compiler/tests/test_common.rs +++ b/external-crates/move/solana/move-mv-llvm-compiler/tests/test_common.rs @@ -146,8 +146,8 @@ pub enum TestDirective { Xfail(String), // The test is expected to fail with the `String` message. It is an error if test passes. Abort(u64), // The test should abort. Log(String), // Test should pass. - Input(Input), - Input2(Input2), + Input(Input), // Input file with program_id, accounts, instruction_data + Input2(Input2),// Input file with program_id, entry_fn. // TODO: Single input.json should be used. UseStdlib, // Build and link the move stdlib as bytecode } @@ -170,6 +170,7 @@ pub struct Input { #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] pub struct Input2 { + // FIXME: We should have single input.json loader with appropriate discriminators. pub program_id: String, pub entry_fn: String, } diff --git a/external-crates/move/solana/move-to-solana/src/entry_codec.rs b/external-crates/move/solana/move-to-solana/src/entry_codec.rs index 5b195ab280e14..91163b2319202 100644 --- a/external-crates/move/solana/move-to-solana/src/entry_codec.rs +++ b/external-crates/move/solana/move-to-solana/src/entry_codec.rs @@ -34,7 +34,7 @@ pub fn generate_move_call_message( fn encode_instruction_data( entry_fn_name: &str, - _args: &[MoveValue], + args: &[MoveValue], ) -> Result> { let mut insn_data: Vec = vec![]; @@ -48,10 +48,10 @@ fn encode_instruction_data( } // encode remaining args of the function - if !_args.is_empty() { - let arglen = _args.len() as u64; + if !args.is_empty() { + let arglen = args.len() as u64; insn_data.extend(&arglen.to_le_bytes()); - for a in _args { + for a in args { // serialize each move value. // TODO: sui-graphql-rpc/src/types/move_value.rs has more // advanced serialization methods to convert to json etc.