diff --git a/Cargo.lock b/Cargo.lock index 7f343e51b..fd5bbb13e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,16 +72,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.1.18" +version = "3.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" +checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", - "lazy_static", + "once_cell", "strsim", "termcolor", "textwrap", @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.18" +version = "3.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" +checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" dependencies = [ "heck", "proc-macro-error", @@ -102,9 +102,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0" dependencies = [ "generic-array", "typenum", @@ -212,9 +212,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" [[package]] name = "heck" @@ -253,20 +253,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.126" @@ -308,9 +302,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-bigint", @@ -327,6 +321,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -335,9 +335,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "os_str_bytes" -version = "6.0.1" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" [[package]] name = "parity-wasm" @@ -377,18 +377,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] @@ -451,9 +451,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.137" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" [[package]] name = "sha2" @@ -514,7 +514,7 @@ dependencies = [ [[package]] name = "stellar-contract-env-common" version = "0.0.0" -source = "git+https://github.com/stellar/rs-stellar-contract-env?rev=42fc83ac#42fc83acacc8d155a92d3b3aca8d917296c58704" +source = "git+https://github.com/stellar/rs-stellar-contract-env?rev=9919061#99190618fe8490bd44dbe03a899cf226e7c8783e" dependencies = [ "static_assertions", "stellar-xdr", @@ -524,13 +524,15 @@ dependencies = [ [[package]] name = "stellar-contract-env-host" version = "0.0.0" -source = "git+https://github.com/stellar/rs-stellar-contract-env?rev=42fc83ac#42fc83acacc8d155a92d3b3aca8d917296c58704" +source = "git+https://github.com/stellar/rs-stellar-contract-env?rev=9919061#99190618fe8490bd44dbe03a899cf226e7c8783e" dependencies = [ "ed25519-dalek", "hex", "im-rc", "num-bigint", + "num-integer", "num-rational", + "num-traits", "parity-wasm", "sha2 0.10.2", "static_assertions", @@ -542,7 +544,7 @@ dependencies = [ [[package]] name = "stellar-xdr" version = "0.0.0" -source = "git+https://github.com/stellar/rs-stellar-xdr?rev=277d41c3#277d41c3c1e1ebd5ad91ff3c190cdb1fe42954c7" +source = "git+https://github.com/stellar/rs-stellar-xdr?rev=db2ca71#db2ca7176740fbd66f9684a1d6a55dfb53a225a5" dependencies = [ "base64", ] @@ -561,9 +563,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.95" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", @@ -625,9 +627,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "unicode-xid" diff --git a/Cargo.toml b/Cargo.toml index 2637d0e15..88dbce342 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] -stellar-contract-env-host = { git = "https://github.com/stellar/rs-stellar-contract-env", rev = "42fc83ac", features = ["vm"] } +stellar-contract-env-host = { git = "https://github.com/stellar/rs-stellar-contract-env", rev = "9919061", features = ["vm"] } # stellar-contract-env-host = { path = "../rs-stellar-contract-env/stellar-contract-env-host", features = ["vm"] } clap = { version = "3.1.18", features = ["derive", "env"] } base64 = "0.13.0" diff --git a/src/invoke.rs b/src/invoke.rs index b226e6938..8d8247396 100644 --- a/src/invoke.rs +++ b/src/invoke.rs @@ -1,7 +1,13 @@ -use std::{fmt::Debug, fs, io}; +use std::{ + fmt::Debug, + fs::{self}, + io, + rc::Rc, +}; use clap::Parser; use stellar_contract_env_host::{ + storage::Storage, xdr::{Error as XdrError, ScVal, ScVec}, Host, HostError, Vm, }; @@ -12,6 +18,8 @@ use crate::strval::{self, StrValError}; pub struct Invoke { #[clap(long, parse(from_os_str))] file: std::path::PathBuf, + #[clap(long, parse(from_os_str), default_value("ledger.xdr"))] + snapshot_file: std::path::PathBuf, #[clap(long = "fn")] function: String, #[clap(long = "arg", multiple_occurrences = true)] @@ -30,10 +38,101 @@ pub enum Error { Host(#[from] HostError), } +pub mod snapshot { + use std::fs::File; + + use super::{Error, HostError}; + use stellar_contract_env_host::{ + im_rc::OrdMap, + storage::SnapshotSource, + xdr::{LedgerEntry, LedgerKey, ReadXdr, WriteXdr}, + }; + + pub struct Snap { + pub ledger_entries: OrdMap, + } + + impl SnapshotSource for Snap { + fn get(&self, key: &LedgerKey) -> Result { + match self.ledger_entries.get(key) { + Some(v) => Ok(v.clone()), + None => Err(HostError::General("missing entry")), + } + } + fn has(&self, key: &LedgerKey) -> Result { + Ok(self.ledger_entries.contains_key(key)) + } + } + + // snapshot_file format is the LedgerKey followed by the + // corresponding LedgerEntry, all in xdr. + // Ex. + // LedgerKey1LedgerEntry1LedgerKey2LedgerEntry2 + // ... + + pub fn read(input_file: &std::path::PathBuf) -> Result, Error> { + let mut res = OrdMap::new(); + + let mut file = match File::open(input_file) { + Ok(f) => f, + Err(e) => { + //File doesn't exist, so treat this as an empty database and the file will be created later + if e.kind() == std::io::ErrorKind::NotFound { + return Ok(res); + } + return Err(Error::Io(e)); + } + }; + + while let (Ok(lk), Ok(le)) = ( + LedgerKey::read_xdr(&mut file), + LedgerEntry::read_xdr(&mut file), + ) { + res.insert(lk, le); + } + + Ok(res) + } + + pub fn commit( + mut new_state: OrdMap, + storage_map: &OrdMap>, + output_file: &std::path::PathBuf, + ) -> Result<(), Error> { + //Need to start off with the existing snapshot (new_state) since it's possible the storage_map did not touch every existing entry + let mut file = File::create(output_file)?; + for (lk, ole) in storage_map { + if let Some(le) = ole { + new_state.insert(lk.clone(), le.clone()); + } else { + new_state.remove(lk); + } + } + + for (lk, le) in new_state { + lk.write_xdr(&mut file)?; + le.write_xdr(&mut file)?; + } + + Ok(()) + } +} + impl Invoke { pub fn run(&self) -> Result<(), Error> { let contents = fs::read(&self.file).unwrap(); - let h = Host::default(); + + // Initialize storage and host + // TODO: allow option to separate input and output file + let ledger_entries = snapshot::read(&self.snapshot_file)?; + let snap = Rc::new(snapshot::Snap { + ledger_entries: ledger_entries.clone(), + }); + let storage = Storage::with_recording_footprint(snap); + + let h = Host::with_storage(storage); + + //TODO: contractID should be user specified let vm = Vm::new(&h, [0; 32].into(), &contents).unwrap(); let args = self .args @@ -43,6 +142,12 @@ impl Invoke { let res = vm.invoke_function(&h, &self.function, &ScVec(args.try_into()?))?; let res_str = strval::to_string(&h, res); println!("{}", res_str); + + let storage = h + .recover_storage() + .map_err(|_h| HostError::General("could not get storage from host"))?; + + snapshot::commit(ledger_entries, &storage.map, &self.snapshot_file)?; Ok(()) } } diff --git a/src/strval.rs b/src/strval.rs index 9c7293e56..c980c1374 100644 --- a/src/strval.rs +++ b/src/strval.rs @@ -65,6 +65,11 @@ pub fn from_string(_h: &Host, s: &str) -> Result { } } "u64" => ScVal::Object(Some(ScObject::U64(val.parse()?))), + "sym" => ScVal::Symbol( + val.as_bytes() + .try_into() + .map_err(|_| StrValError::InvalidValue)?, + ), _ => return Err(StrValError::UnknownType), }; Ok(val) @@ -76,8 +81,11 @@ pub fn to_string(_h: &Host, v: ScVal) -> String { ScVal::I32(v) => format!("i32:{}", v), ScVal::U32(v) => format!("u32:{}", v), ScVal::U63(v) => format!("i64:{}", v), - ScVal::Static(_) => todo!(), - ScVal::Symbol(_) => todo!(), + ScVal::Static(_) => "todo!".to_string(), //TODO:fix this + ScVal::Symbol(v) => format!( + "sym:{}", + std::str::from_utf8(v.as_slice()).expect("non-UTF-8 in symbol") + ), ScVal::Bitset(_) => todo!(), ScVal::Status(_) => todo!(), ScVal::Object(None) => panic!(""),