Skip to content

Commit

Permalink
Take a stab at extracting the spec from the state
Browse files Browse the repository at this point in the history
  • Loading branch information
2opremio committed Sep 1, 2023
1 parent ff1a045 commit 315b6b1
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 54 deletions.
24 changes: 24 additions & 0 deletions cmd/crates/soroban-test/tests/it/invoke_sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,19 @@ fn invoke_auth() {
.assert()
.stdout(format!("\"{DEFAULT_PUB_KEY}\"\n"))
.success();

// Invoke it again without providing the contract, to exercise the deployment
sandbox
.new_assert_cmd("contract")
.arg("invoke")
.arg("--id=1")
.arg("--")
.arg("auth")
.arg(&format!("--addr={DEFAULT_PUB_KEY}"))
.arg("--world=world")
.assert()
.stdout(format!("\"{DEFAULT_PUB_KEY}\"\n"))
.success();
}

#[tokio::test]
Expand Down Expand Up @@ -290,6 +303,17 @@ fn invoke_with_source(sandbox: &TestEnv, source: &str) {
"--world=world",
]);
assert_eq!(cmd.unwrap(), "[\"Hello\",\"world\"]");

// Invoke it again without providing the wasm
let cmd = sandbox.invoke(&[
"--source-account",
source,
"--id=1",
"--",
"hello",
"--world=world",
]);
assert_eq!(cmd.unwrap(), "[\"Hello\",\"world\"]");
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions cmd/soroban-cli/src/commands/contract/invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,11 @@ impl Cmd {
}

let snap = Rc::new(state.clone());
let mut storage = Storage::with_recording_footprint(snap);
let storage = Storage::with_recording_footprint(snap);
let spec_entries = if let Some(spec) = self.spec_entries()? {
spec
} else {
utils::get_contract_spec_from_storage(&mut storage, &state.sequence_number, contract_id)
utils::get_contract_spec_from_state(&state, contract_id)
.map_err(Error::CannotParseContractSpec)?
};
let budget = Budget::default();
Expand Down
111 changes: 59 additions & 52 deletions cmd/soroban-cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use ed25519_dalek::Signer;
use sha2::{Digest, Sha256};

use soroban_env_host::{
budget::Budget,
storage::{AccessType, Footprint, Storage},
storage::{AccessType, Footprint},
xdr::{
AccountEntry, AccountEntryExt, AccountId, Asset, ContractCodeEntry, ContractDataDurability,
ContractDataEntry, ContractExecutable, ContractIdPreimage, DecoratedSignature,
Expand Down Expand Up @@ -199,68 +198,76 @@ pub fn contract_id_from_str(contract_id: &str) -> Result<[u8; 32], stellar_strke
.map_err(|_| stellar_strkey::DecodeError::Invalid)
}

fn get_entry_from_spanshot(
key: &LedgerKey,
entries: &LedgerSnapshotEntries,
) -> Option<(Box<LedgerEntry>, Option<u32>)> {
for (k, result) in entries {
if *key == **k {
return Some((*result).clone());
}
}
None
}

/// # Errors
///
/// Might return an error
pub fn get_contract_spec_from_storage(
storage: &mut Storage,
_current_ledger_seq: &u32,
pub fn get_contract_spec_from_state(
state: &LedgerSnapshot,
contract_id: [u8; 32],
) -> Result<Vec<ScSpecEntry>, FromWasmError> {
let current_ledger_seq = state.sequence_number;
let key = LedgerKey::ContractData(LedgerKeyContractData {
contract: ScAddress::Contract(contract_id.into()),
key: ScVal::LedgerKeyContractInstance,
durability: ContractDataDurability::Persistent,
});
match storage.get(&key.into(), &Budget::default()) {
Ok(rc) => match rc.as_ref() {
LedgerEntry {
data:
LedgerEntryData::ContractData(ContractDataEntry {
val: ScVal::ContractInstance(ScContractInstance { executable, .. }),
..
}),
..
} => match executable {
ContractExecutable::Token => {
// TODO: How to identify that an entry is expired now?
// I(fons) don't think that the storage contains expiration entries and I don't
// think we can get it from the state in all cases (e.g. Token contracts)
// if expiration_ledger_seq <= current_ledger_seq {
// return Err(FromWasmError::NotFound);
// }
let res = soroban_spec::read::parse_raw(&token::StellarAssetSpec::spec_xdr());
res.map_err(FromWasmError::Parse)
let (entry, expiration_ledger_seq) = match get_entry_from_spanshot(&key, &state.ledger_entries)
{
// It's a contract data entry, so it should have an expiration if present
Some((entry, expiration)) => (entry, expiration.unwrap()),
None => return Err(FromWasmError::NotFound),
};
if expiration_ledger_seq <= current_ledger_seq {
return Err(FromWasmError::NotFound);
}

match *entry {
LedgerEntry {
data:
LedgerEntryData::ContractData(ContractDataEntry {
val: ScVal::ContractInstance(ScContractInstance { executable, .. }),
..
}),
..
} => match executable {
ContractExecutable::Token => {
// TODO/FIXME: I don't think it will work for token contracts, since we don't store them in the state?
let res = soroban_spec::read::parse_raw(&token::StellarAssetSpec::spec_xdr());
res.map_err(FromWasmError::Parse)
}
ContractExecutable::Wasm(hash) => {
// It's a contract code entry, so it should have an expiration if present
let (entry, expiration_ledger_seq) = match get_entry_from_spanshot(
&LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }),
&state.ledger_entries,
) {
// It's a contract data entry, so it should have an expiration if present
Some((entry, expiration)) => (entry, expiration.unwrap()),
None => return Err(FromWasmError::NotFound),
};
if expiration_ledger_seq <= current_ledger_seq {
return Err(FromWasmError::NotFound);
}
ContractExecutable::Wasm(hash) => {
// TODO: How to identify that entry is expired now?
// if expiration_ledger_seq <= current_ledger_seq {
// return Err(FromWasmError::NotFound);
// }
if let Ok(rc) = storage.get(
&LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() })
.into(),
&Budget::default(),
) {
match rc.as_ref() {
LedgerEntry {
data: LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }),
..
} => {
// TODO: How to identify that entry is expired now?
// if expiration_ledger_seq <= current_ledger_seq {
// return Err(FromWasmError::NotFound);
// }
soroban_spec::read::from_wasm(code.as_vec())
}
_ => Err(FromWasmError::NotFound),
}
} else {
Err(FromWasmError::NotFound)
}
match *entry {
LedgerEntry {
data: LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }),
..
} => soroban_spec::read::from_wasm(code.as_vec()),
_ => Err(FromWasmError::NotFound),
}
},
_ => Err(FromWasmError::NotFound),
}
},
_ => Err(FromWasmError::NotFound),
}
Expand Down

0 comments on commit 315b6b1

Please sign in to comment.