diff --git a/.github/workflows/soroban-rpc.yml b/.github/workflows/soroban-rpc.yml index 9022e877e..8ee0898d2 100644 --- a/.github/workflows/soroban-rpc.yml +++ b/.github/workflows/soroban-rpc.yml @@ -110,7 +110,7 @@ jobs: env: SOROBAN_RPC_INTEGRATION_TESTS_ENABLED: true SOROBAN_RPC_INTEGRATION_TESTS_CAPTIVE_CORE_BIN: /usr/bin/stellar-core - PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.13.1-1458.431e4e324.focal~soroban~settings~override + PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.13.1-1462.22b9bb384.focal~vnext runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 diff --git a/Cargo.lock b/Cargo.lock index acf4eadaf..af31dfd1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2564,7 +2564,7 @@ dependencies = [ [[package]] name = "soroban-env-common" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "arbitrary", "crate-git-revision 0.0.6", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "soroban-env-guest" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "soroban-env-common", "static_assertions", @@ -2590,7 +2590,7 @@ dependencies = [ [[package]] name = "soroban-env-host" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "backtrace", "curve25519-dalek 4.0.0", @@ -2616,7 +2616,7 @@ dependencies = [ [[package]] name = "soroban-env-macros" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "itertools", "proc-macro2", @@ -2634,7 +2634,7 @@ version = "0.9.4" [[package]] name = "soroban-ledger-snapshot" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "serde", "serde_json", @@ -2646,7 +2646,7 @@ dependencies = [ [[package]] name = "soroban-native-sdk-macros" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "itertools", "proc-macro2", @@ -2657,7 +2657,7 @@ dependencies = [ [[package]] name = "soroban-sdk" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "arbitrary", "bytes-lit", @@ -2674,7 +2674,7 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "crate-git-revision 0.0.6", "darling", @@ -2693,7 +2693,7 @@ dependencies = [ [[package]] name = "soroban-spec" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "base64 0.13.1", "stellar-xdr", @@ -2718,7 +2718,7 @@ dependencies = [ [[package]] name = "soroban-spec-rust" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "prettyplease", "proc-macro2", @@ -2854,7 +2854,7 @@ dependencies = [ [[package]] name = "stellar-xdr" version = "0.0.17" -source = "git+https://github.com/stellar/rs-stellar-xdr?rev=e2a9cbf72d94941de1bde6ba34a38e1f49328567#e2a9cbf72d94941de1bde6ba34a38e1f49328567" +source = "git+https://github.com/stellar/rs-stellar-xdr?rev=39904e09941046dab61e6e35fc89e31bf2dea1cd#39904e09941046dab61e6e35fc89e31bf2dea1cd" dependencies = [ "arbitrary", "base64 0.13.1", diff --git a/Cargo.toml b/Cargo.toml index 876a93f21..0c9bb5419 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,18 +16,18 @@ version = "0.9.4" [workspace.dependencies.soroban-env-host] version = "0.0.17" git = "https://github.com/stellar/rs-soroban-env" -rev = "ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +rev = "eb5a9ba053a7b64a8eff605db625525378f7bea0" [workspace.dependencies.soroban-spec] version = "0.9.1" -git = "https://github.com/sisuresh/rs-soroban-sdk" -rev = "e15e552974a1070fc48027384bdee9ae9f539943" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "18b8fa1a358aa84afd196e2f6d44942798c8f335" # path = "../rs-soroban-sdk/soroban-spec" [workspace.dependencies.soroban-spec-rust] version = "0.9.1" -git = "https://github.com/sisuresh/rs-soroban-sdk" -rev = "e15e552974a1070fc48027384bdee9ae9f539943" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "18b8fa1a358aa84afd196e2f6d44942798c8f335" # path = "../rs-soroban-sdk/soroban-spec-rust" [workspace.dependencies.soroban-spec-json] @@ -44,13 +44,13 @@ path = "./cmd/crates/soroban-spec-tools" [workspace.dependencies.soroban-sdk] version = "0.9.1" -git = "https://github.com/sisuresh/rs-soroban-sdk" -rev = "e15e552974a1070fc48027384bdee9ae9f539943" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "18b8fa1a358aa84afd196e2f6d44942798c8f335" [workspace.dependencies.soroban-ledger-snapshot] version = "0.9.1" -git = "https://github.com/sisuresh/rs-soroban-sdk" -rev = "e15e552974a1070fc48027384bdee9ae9f539943" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "18b8fa1a358aa84afd196e2f6d44942798c8f335" [workspace.dependencies.soroban-cli] version = "0.9.4" @@ -59,7 +59,7 @@ path = "cmd/soroban-cli" [workspace.dependencies.stellar-xdr] version = "0.0.17" git = "https://github.com/stellar/rs-stellar-xdr" -rev = "e2a9cbf72d94941de1bde6ba34a38e1f49328567" +rev = "39904e09941046dab61e6e35fc89e31bf2dea1cd" default-features = false [workspace.dependencies] diff --git a/cmd/crates/soroban-test/tests/it/invoke_sandbox.rs b/cmd/crates/soroban-test/tests/it/invoke_sandbox.rs index 821e2bf2f..032df2a8c 100644 --- a/cmd/crates/soroban-test/tests/it/invoke_sandbox.rs +++ b/cmd/crates/soroban-test/tests/it/invoke_sandbox.rs @@ -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] @@ -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 contract, to exercise the deployment + let cmd = sandbox.invoke(&[ + "--source-account", + source, + "--id=1", + "--", + "hello", + "--world=world", + ]); + assert_eq!(cmd.unwrap(), "[\"Hello\",\"world\"]"); } #[test] diff --git a/cmd/soroban-cli/src/commands/contract/bump.rs b/cmd/soroban-cli/src/commands/contract/bump.rs index f3b34fd64..1a3aefe35 100644 --- a/cmd/soroban-cli/src/commands/contract/bump.rs +++ b/cmd/soroban-cli/src/commands/contract/bump.rs @@ -6,12 +6,12 @@ use std::{ use clap::{command, Parser}; use soroban_env_host::xdr::{ - BumpFootprintExpirationOp, ContractCodeEntry, ContractDataEntry, ContractEntryBodyType, - Error as XdrError, ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData, - LedgerFootprint, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, - Operation, OperationBody, Preconditions, ReadXdr, ScAddress, ScSpecTypeDef, ScVal, - SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, TransactionExt, - TransactionMeta, TransactionMetaV3, Uint256, + BumpFootprintExpirationOp, Error as XdrError, ExpirationEntry, ExtensionPoint, Hash, + LedgerEntry, LedgerEntryChange, LedgerEntryData, LedgerFootprint, LedgerKey, + LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, Operation, OperationBody, + Preconditions, ReadXdr, ScAddress, ScSpecTypeDef, ScVal, SequenceNumber, SorobanResources, + SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, TransactionMetaV3, + Uint256, }; use stellar_strkey::DecodeError; @@ -195,16 +195,13 @@ impl Cmd { return Err(Error::LedgerEntryNotFound); } + // TODO: double-check that this match is correct match (&operations[0].changes[0], &operations[0].changes[1]) { ( LedgerEntryChange::State(_), LedgerEntryChange::Updated(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { - expiration_ledger_seq, - .. - }) - | LedgerEntryData::ContractCode(ContractCodeEntry { + LedgerEntryData::Expiration(ExpirationEntry { expiration_ledger_seq, .. }), @@ -229,16 +226,21 @@ impl Cmd { .iter() .map(|(k, v)| { let new_k = k.as_ref().clone(); - let new_v = v.as_ref().clone(); + let new_v = v.0.as_ref().clone(); + let new_e = v.1; ( Box::new(new_k.clone()), - Box::new(if needle == new_k { - let (new_v, new_expiration) = bump_entry(&new_v, self.ledgers_to_expire); - expiration_ledger_seq = Some(new_expiration); - new_v - } else { - new_v - }), + ( + Box::new(new_v), + if needle == new_k { + // It must have an expiration since it's a contract data entry + let old_expiration = v.1.unwrap(); + expiration_ledger_seq = Some(old_expiration + self.ledgers_to_expire); + expiration_ledger_seq + } else { + new_e + }, + ), ) }) .collect::>(); @@ -273,7 +275,6 @@ impl Cmd { utils::contract_id_from_str(wasm_hash) .map_err(|e| Error::CannotParseContractId(wasm_hash.clone(), e))?, ), - body_type: ContractEntryBodyType::DataEntry, })); } else { ScVal::LedgerKeyContractInstance @@ -283,25 +284,11 @@ impl Cmd { Ok(LedgerKey::ContractData(LedgerKeyContractData { contract: ScAddress::Contract(Hash(contract_id)), durability: self.durability.into(), - body_type: ContractEntryBodyType::DataEntry, key, })) } } -fn bump_entry(v: &LedgerEntry, ledgers_to_expire: u32) -> (LedgerEntry, u32) { - let mut new_v = v.clone(); - let mut new_expiration_ledger_seq = 0; - if let LedgerEntryData::ContractData(ref mut data) = new_v.data { - data.expiration_ledger_seq += ledgers_to_expire; - new_expiration_ledger_seq = data.expiration_ledger_seq; - } else if let LedgerEntryData::ContractCode(ref mut code) = new_v.data { - code.expiration_ledger_seq += ledgers_to_expire; - new_expiration_ledger_seq = code.expiration_ledger_seq; - } - (new_v, new_expiration_ledger_seq) -} - fn contract_id(s: &str) -> Result<[u8; 32], Error> { utils::contract_id_from_str(s).map_err(|e| Error::CannotParseContractId(s.to_string(), e)) } diff --git a/cmd/soroban-cli/src/commands/contract/fetch.rs b/cmd/soroban-cli/src/commands/contract/fetch.rs index 7508daf8c..a1284123e 100644 --- a/cmd/soroban-cli/src/commands/contract/fetch.rs +++ b/cmd/soroban-cli/src/commands/contract/fetch.rs @@ -10,8 +10,7 @@ use soroban_env_host::{ budget::Budget, storage::Storage, xdr::{ - self, ContractCodeEntry, ContractCodeEntryBody, ContractDataDurability, ContractDataEntry, - ContractDataEntryBody, ContractDataEntryData, ContractEntryBodyType, ContractExecutable, + self, ContractCodeEntry, ContractDataDurability, ContractDataEntry, ContractExecutable, Error as XdrError, LedgerEntryData, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, ScAddress, ScContractInstance, ScVal, }, @@ -171,38 +170,26 @@ pub fn get_contract_wasm_from_storage( contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, }); match storage.get(&key.into(), &Budget::default()) { Ok(rc) => match rc.as_ref() { xdr::LedgerEntry { data: LedgerEntryData::ContractData(ContractDataEntry { - body: - ContractDataEntryBody::DataEntry(ContractDataEntryData { - val: ScVal::ContractInstance(ScContractInstance { executable, .. }), - .. - }), + val: ScVal::ContractInstance(ScContractInstance { executable, .. }), .. }), .. } => match executable { ContractExecutable::Wasm(hash) => { if let Ok(rc) = storage.get( - &LedgerKey::ContractCode(LedgerKeyContractCode { - hash: hash.clone(), - body_type: ContractEntryBodyType::DataEntry, - }) - .into(), + &LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }) + .into(), &Budget::default(), ) { match rc.as_ref() { xdr::LedgerEntry { - data: - LedgerEntryData::ContractCode(ContractCodeEntry { - body: ContractCodeEntryBody::DataEntry(code), - .. - }), + data: LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }), .. } => Ok(code.to_vec()), _ => Err(FromWasmError::NotFound), diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 4afed4192..11855b7af 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -9,7 +9,7 @@ use std::{fmt::Debug, fs, io, rc::Rc}; use clap::{arg, command, value_parser, Parser}; use ed25519_dalek::Keypair; use heck::ToKebabCase; -use soroban_env_host::e2e_invoke::get_ledger_changes; +use soroban_env_host::e2e_invoke::{get_ledger_changes, ExpirationEntryMap}; use soroban_env_host::xdr::ReadXdr; use soroban_env_host::{ budget::Budget, @@ -360,16 +360,19 @@ impl Cmd { { state.ledger_entries.push(( Box::new(source_account_ledger_key), - Box::new(default_account_ledger_entry(source_account.clone())), + ( + Box::new(default_account_ledger_entry(source_account.clone())), + None, + ), )); } 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(); @@ -430,7 +433,8 @@ impl Cmd { log_budget(&budget); } - let ledger_changes = get_ledger_changes(&budget, &storage, &state)?; + let ledger_changes = + get_ledger_changes(&budget, &storage, &state, ExpirationEntryMap::new())?; let mut expiration_ledger_bumps: HashMap = HashMap::new(); for ledger_entry_change in ledger_changes { if let Some(exp_change) = ledger_entry_change.expiration_change { diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs index eb074c8ee..8c00244f7 100644 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ b/cmd/soroban-cli/src/commands/contract/read.rs @@ -7,8 +7,7 @@ use std::{ use clap::{command, Parser, ValueEnum}; use soroban_env_host::{ xdr::{ - self, ContractDataDurability, ContractDataEntry, ContractDataEntryBody, - ContractDataEntryData, ContractEntryBodyType, Error as XdrError, Hash, LedgerEntryData, + self, ContractDataDurability, ContractDataEntry, Error as XdrError, Hash, LedgerEntryData, LedgerKey, LedgerKeyContractData, ReadXdr, ScAddress, ScSpecTypeDef, ScVal, WriteXdr, }, HostError, @@ -163,7 +162,6 @@ impl Cmd { contract: ScAddress::Contract(Hash(contract_id)), key: key.clone(), durability: (*durability).into(), - body_type: ContractEntryBodyType::DataEntry, }) }) .collect::>(); @@ -202,7 +200,7 @@ impl Cmd { Ok(ledger_entries .iter() - .map(|(k, v)| (k.as_ref().clone(), v.as_ref().clone())) + .map(|(k, v)| (k.as_ref().clone(), (v.0.as_ref().clone(), v.1))) .filter(|(k, _v)| { if let LedgerKey::ContractData(LedgerKeyContractData { contract: c, .. }) = k { if c == &contract { @@ -211,14 +209,6 @@ impl Cmd { } false }) - .filter(|(k, _v)| { - if let LedgerKey::ContractData(LedgerKeyContractData { body_type, .. }) = k { - if body_type == &ContractEntryBodyType::DataEntry { - return true; - } - } - false - }) .filter(|(k, _v)| { if key.is_none() { return true; @@ -241,7 +231,7 @@ impl Cmd { } false }) - .map(|(k, v)| (k, v.data)) + .map(|(k, (v, _))| (k, v.data)) .collect::>()) } @@ -249,12 +239,7 @@ impl Cmd { let entries = raw_entries .iter() .filter_map(|(_k, data)| { - if let LedgerEntryData::ContractData(ContractDataEntry { - key, - body: ContractDataEntryBody::DataEntry(ContractDataEntryData { val, .. }), - .. - }) = &data - { + if let LedgerEntryData::ContractData(ContractDataEntry { key, val, .. }) = &data { Some((key.clone(), val.clone())) } else { None diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index f74855ead..2834c1995 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -6,12 +6,12 @@ use std::{ use clap::{command, Parser}; use soroban_env_host::xdr::{ - ContractCodeEntry, ContractDataDurability, ContractDataEntry, ContractEntryBodyType, - Error as XdrError, ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData, - LedgerFootprint, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, - Operation, OperationBody, OperationMeta, Preconditions, ReadXdr, RestoreFootprintOp, ScAddress, - ScSpecTypeDef, ScVal, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, - TransactionExt, TransactionMeta, TransactionMetaV3, Uint256, + ContractDataDurability, Error as XdrError, ExpirationEntry, ExtensionPoint, Hash, LedgerEntry, + LedgerEntryChange, LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyContractCode, + LedgerKeyContractData, Memo, MuxedAccount, Operation, OperationBody, OperationMeta, + Preconditions, ReadXdr, RestoreFootprintOp, ScAddress, ScSpecTypeDef, ScVal, SequenceNumber, + SorobanResources, SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, + TransactionMetaV3, Uint256, }; use stellar_strkey::DecodeError; @@ -133,7 +133,6 @@ impl Cmd { utils::contract_id_from_str(wasm_hash) .map_err(|e| Error::CannotParseContractId(wasm_hash.clone(), e))?, ), - body_type: ContractEntryBodyType::DataEntry, })] } else { let contract_id = self.contract_id()?; @@ -250,7 +249,6 @@ impl Cmd { LedgerKey::ContractData(LedgerKeyContractData { contract: ScAddress::Contract(Hash(contract_id)), durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, key: key.clone(), }) }) @@ -263,11 +261,7 @@ fn parse_operations(ops: &[OperationMeta]) -> Option { op.changes.iter().find_map(|entry| match entry { LedgerEntryChange::Updated(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { - expiration_ledger_seq, - .. - }) - | LedgerEntryData::ContractCode(ContractCodeEntry { + LedgerEntryData::Expiration(ExpirationEntry { expiration_ledger_seq, .. }), @@ -275,11 +269,7 @@ fn parse_operations(ops: &[OperationMeta]) -> Option { }) | LedgerEntryChange::Created(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { - expiration_ledger_seq, - .. - }) - | LedgerEntryData::ContractCode(ContractCodeEntry { + LedgerEntryData::Expiration(ExpirationEntry { expiration_ledger_seq, .. }), diff --git a/cmd/soroban-cli/src/commands/events.rs b/cmd/soroban-cli/src/commands/events.rs index 16a097334..ad9055e2d 100644 --- a/cmd/soroban-cli/src/commands/events.rs +++ b/cmd/soroban-cli/src/commands/events.rs @@ -344,7 +344,6 @@ mod tests { max_entry_expiration: 6, min_persistent_entry_expiration: 7, min_temp_entry_expiration: 8, - autobump_ledgers: 16, }; events_file.commit(&events, &ledger_info, &temp).unwrap(); diff --git a/cmd/soroban-cli/src/commands/lab/token/wrap.rs b/cmd/soroban-cli/src/commands/lab/token/wrap.rs index aa69ceeb2..799dc618f 100644 --- a/cmd/soroban-cli/src/commands/lab/token/wrap.rs +++ b/cmd/soroban-cli/src/commands/lab/token/wrap.rs @@ -3,11 +3,10 @@ use soroban_env_host::{ budget::Budget, storage::Storage, xdr::{ - Asset, ContractDataDurability, ContractEntryBodyType, ContractExecutable, - ContractIdPreimage, CreateContractArgs, Error as XdrError, Hash, HostFunction, - InvokeHostFunctionOp, LedgerKey::ContractData, LedgerKeyContractData, Memo, MuxedAccount, - Operation, OperationBody, Preconditions, ScAddress, ScVal, SequenceNumber, Transaction, - TransactionExt, Uint256, VecM, + Asset, ContractDataDurability, ContractExecutable, ContractIdPreimage, CreateContractArgs, + Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, LedgerKey::ContractData, + LedgerKeyContractData, Memo, MuxedAccount, Operation, OperationBody, Preconditions, + ScAddress, ScVal, SequenceNumber, Transaction, TransactionExt, Uint256, VecM, }, Host, HostError, }; @@ -162,7 +161,6 @@ fn build_wrap_token_tx( contract: contract.clone(), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, }), ContractData(LedgerKeyContractData { contract: contract.clone(), @@ -170,7 +168,6 @@ fn build_wrap_token_tx( vec![ScVal::Symbol("Metadata".try_into().unwrap())].try_into()?, )), durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, }), ]; if asset != &Asset::Native { @@ -180,7 +177,6 @@ fn build_wrap_token_tx( vec![ScVal::Symbol("Admin".try_into().unwrap())].try_into()?, )), durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, })); } diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs index 0fe2df333..451b4b04e 100644 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ b/cmd/soroban-cli/src/rpc/mod.rs @@ -744,7 +744,6 @@ soroban config identity fund {address} --helper-url "# contract: xdr::ScAddress::Contract(xdr::Hash(*contract_id)), key: xdr::ScVal::LedgerKeyContractInstance, durability: xdr::ContractDataDurability::Persistent, - body_type: xdr::ContractEntryBodyType::DataEntry, }); let contract_ref = self.get_ledger_entries(&[contract_key]).await?; let entries = contract_ref.entries.unwrap_or_default(); @@ -762,13 +761,9 @@ soroban config identity fund {address} --helper-url "# pub async fn get_remote_wasm(&self, contract_id: &[u8; 32]) -> Result, Error> { match self.get_contract_data(contract_id).await? { xdr::ContractDataEntry { - body: - xdr::ContractDataEntryBody::DataEntry(xdr::ContractDataEntryData { - val: - xdr::ScVal::ContractInstance(xdr::ScContractInstance { - executable: xdr::ContractExecutable::Wasm(hash), - .. - }), + val: + xdr::ScVal::ContractInstance(xdr::ScContractInstance { + executable: xdr::ContractExecutable::Wasm(hash), .. }), .. @@ -778,10 +773,7 @@ soroban config identity fund {address} --helper-url "# } pub async fn get_remote_wasm_from_hash(&self, hash: xdr::Hash) -> Result, Error> { - let code_key = LedgerKey::ContractCode(xdr::LedgerKeyContractCode { - hash: hash.clone(), - body_type: xdr::ContractEntryBodyType::DataEntry, - }); + let code_key = LedgerKey::ContractCode(xdr::LedgerKeyContractCode { hash: hash.clone() }); let contract_data = self.get_ledger_entries(&[code_key]).await?; let entries = contract_data.entries.unwrap_or_default(); if entries.is_empty() { @@ -792,10 +784,7 @@ soroban config identity fund {address} --helper-url "# } let contract_data_entry = &entries[0]; match LedgerEntryData::from_xdr_base64(&contract_data_entry.xdr)? { - LedgerEntryData::ContractCode(xdr::ContractCodeEntry { - body: xdr::ContractCodeEntryBody::DataEntry(code), - .. - }) => Ok(code.into()), + LedgerEntryData::ContractCode(xdr::ContractCodeEntry { code, .. }) => Ok(code.into()), scval => Err(Error::UnexpectedContractCodeDataType(scval)), } } @@ -805,10 +794,7 @@ soroban config identity fund {address} --helper-url "# contract_id: &[u8; 32], ) -> Result, Error> { let contract_data = self.get_contract_data(contract_id).await?; - let xdr::ContractDataEntryBody::DataEntry(data) = contract_data.body else { - return Err(Error::Xdr(XdrError::Invalid)); - }; - match data.val { + match contract_data.val { xdr::ScVal::ContractInstance(xdr::ScContractInstance { executable: xdr::ContractExecutable::Wasm(hash), .. diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index 3792c63d7..90a051fa4 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -5,12 +5,10 @@ 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, ContractCodeEntryBody, - ContractDataDurability, ContractDataEntry, ContractDataEntryBody, ContractDataEntryData, - ContractEntryBodyType, ContractExecutable, ContractIdPreimage, DecoratedSignature, + AccountEntry, AccountEntryExt, AccountId, Asset, ContractCodeEntry, ContractDataDurability, + ContractDataEntry, ContractExecutable, ContractIdPreimage, DecoratedSignature, Error as XdrError, ExtensionPoint, Hash, HashIdPreimage, HashIdPreimageContractId, LedgerEntry, LedgerEntryData, LedgerEntryExt, LedgerFootprint, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, ScAddress, ScContractInstance, ScSpecEntry, @@ -60,42 +58,43 @@ pub fn ledger_snapshot_read_or_default( } } +type LedgerSnapshotEntries = Vec<(Box, (Box, Option))>; + /// # Errors /// /// Might return an error pub fn add_contract_code_to_ledger_entries( - entries: &mut Vec<(Box, Box)>, + entries: &mut LedgerSnapshotEntries, contract: Vec, min_persistent_entry_expiration: u32, ) -> Result { // Install the code let hash = contract_hash(contract.as_slice())?; - let code_key = LedgerKey::ContractCode(LedgerKeyContractCode { - hash: hash.clone(), - body_type: ContractEntryBodyType::DataEntry, - }); + let code_key = LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }); let code_entry = LedgerEntry { last_modified_ledger_seq: 0, data: LedgerEntryData::ContractCode(ContractCodeEntry { ext: ExtensionPoint::V0, hash: hash.clone(), - body: ContractCodeEntryBody::DataEntry(contract.try_into()?), - expiration_ledger_seq: min_persistent_entry_expiration, + code: contract.try_into()?, }), ext: LedgerEntryExt::V0, }; for (k, e) in &mut *entries { if **k == code_key { - **e = code_entry; + *e = (Box::new(code_entry), Some(min_persistent_entry_expiration)); return Ok(hash); } } - entries.push((Box::new(code_key), Box::new(code_entry))); + entries.push(( + Box::new(code_key), + (Box::new(code_entry), Some(min_persistent_entry_expiration)), + )); Ok(hash) } pub fn add_contract_to_ledger_entries( - entries: &mut Vec<(Box, Box)>, + entries: &mut LedgerSnapshotEntries, contract_id: [u8; 32], wasm_hash: [u8; 32], min_persistent_entry_expiration: u32, @@ -105,7 +104,6 @@ pub fn add_contract_to_ledger_entries( contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, }); let contract_entry = LedgerEntry { @@ -114,35 +112,39 @@ pub fn add_contract_to_ledger_entries( contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, - body: ContractDataEntryBody::DataEntry(ContractDataEntryData { - flags: 0, - val: ScVal::ContractInstance(ScContractInstance { - executable: ContractExecutable::Wasm(Hash(wasm_hash)), - storage: None, - }), + val: ScVal::ContractInstance(ScContractInstance { + executable: ContractExecutable::Wasm(Hash(wasm_hash)), + storage: None, }), - expiration_ledger_seq: min_persistent_entry_expiration, + ext: ExtensionPoint::V0, }), ext: LedgerEntryExt::V0, }; for (k, e) in &mut *entries { if **k == contract_key { - **e = contract_entry; + *e = ( + Box::new(contract_entry), + Some(min_persistent_entry_expiration), + ); return; } } - entries.push((Box::new(contract_key), Box::new(contract_entry))); + entries.push(( + Box::new(contract_key), + ( + Box::new(contract_entry), + Some(min_persistent_entry_expiration), + ), + )); } pub fn bump_ledger_entry_expirations( - entries: &mut [(Box, Box)], + entries: &mut LedgerSnapshotEntries, lookup: &HashMap, ) { - for (k, e) in &mut *entries { + for (k, (_, expiration)) in &mut *entries { if let Some(min_expiration) = lookup.get(k.as_ref()) { - if let LedgerEntryData::ContractData(entry) = &mut e.data { - entry.expiration_ledger_seq = *min_expiration; - } + *expiration = Some(*min_expiration); } } } @@ -196,77 +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_snapshot( + key: &LedgerKey, + entries: &LedgerSnapshotEntries, +) -> Option<(Box, Option)> { + 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, FromWasmError> { + let current_ledger_seq = state.sequence_number; let key = LedgerKey::ContractData(LedgerKeyContractData { contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, - body_type: ContractEntryBodyType::DataEntry, durability: ContractDataDurability::Persistent, }); - match storage.get(&key.into(), &Budget::default()) { - Ok(rc) => match rc.as_ref() { - LedgerEntry { - data: - LedgerEntryData::ContractData(ContractDataEntry { - body: - ContractDataEntryBody::DataEntry(ContractDataEntryData { - val: ScVal::ContractInstance(ScContractInstance { executable, .. }), - .. - }), - expiration_ledger_seq, - .. - }), - .. - } => match executable { - ContractExecutable::Token => { - 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_snapshot(&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_snapshot( + &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) => { - if expiration_ledger_seq <= current_ledger_seq { - return Err(FromWasmError::NotFound); - } - if let Ok(rc) = storage.get( - &LedgerKey::ContractCode(LedgerKeyContractCode { - hash: hash.clone(), - body_type: ContractEntryBodyType::DataEntry, - }) - .into(), - &Budget::default(), - ) { - match rc.as_ref() { - LedgerEntry { - data: - LedgerEntryData::ContractCode(ContractCodeEntry { - body: ContractCodeEntryBody::DataEntry(code), - expiration_ledger_seq, - .. - }), - .. - } => { - 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), } diff --git a/cmd/soroban-cli/src/wasm.rs b/cmd/soroban-cli/src/wasm.rs index 428a68e93..fce44c7c5 100644 --- a/cmd/soroban-cli/src/wasm.rs +++ b/cmd/soroban-cli/src/wasm.rs @@ -1,5 +1,5 @@ use clap::arg; -use soroban_env_host::xdr::{self, ContractEntryBodyType, LedgerKey, LedgerKeyContractCode}; +use soroban_env_host::xdr::{self, LedgerKey, LedgerKeyContractCode}; use std::{ fs, io, path::{Path, PathBuf}, @@ -77,7 +77,6 @@ impl TryInto for Args { fn try_into(self) -> Result { Ok(LedgerKey::ContractCode(LedgerKeyContractCode { hash: utils::contract_hash(&self.read()?)?, - body_type: ContractEntryBodyType::DataEntry, })) } } diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 6363e2518..8bb6a68cb 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -3,7 +3,6 @@ package db import ( "context" "database/sql" - "encoding/base64" "fmt" sq "github.com/Masterminds/squirrel" @@ -30,12 +29,11 @@ type LedgerKeyAndEntry struct { type LedgerEntryReadTx interface { GetLatestLedgerSequence() (uint32, error) - GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) + GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) Done() error } type LedgerEntryWriter interface { - ExtendLedgerEntry(key xdr.LedgerKey, expirationLedgerSeq xdr.Uint32) error UpsertLedgerEntry(entry xdr.LedgerEntry) error DeleteLedgerEntry(key xdr.LedgerKey) error } @@ -49,52 +47,6 @@ type ledgerEntryWriter struct { maxBatchSize int } -func (l ledgerEntryWriter) ExtendLedgerEntry(key xdr.LedgerKey, expirationLedgerSeq xdr.Uint32) error { - // TODO: How do we figure out the current expiration? We might need to read - // from the DB, but in the case of creating a new entry and immediately - // extending it, or extending multiple times in the same ledger, the - // expirationLedgerSeq might be buffered but not flushed yet. - if key.Type != xdr.LedgerEntryTypeContractCode && key.Type != xdr.LedgerEntryTypeContractData { - panic("ExtendLedgerEntry can only be used for contract code and data") - } - - encodedKey, err := encodeLedgerKey(l.buffer, key) - if err != nil { - return err - } - - var entry xdr.LedgerEntry - // See if we have a pending (unflushed) update for this key - queued := l.keyToEntryBatch[encodedKey] - if queued != nil { - entry = *queued - } else { - var existing string - // Nothing in the flush buffer. Load the entry from the db - err = sq.StatementBuilder.RunWith(l.stmtCache).Select("entry").From(ledgerEntriesTableName).Where(sq.Eq{"key": encodedKey}).QueryRow().Scan(&existing) - if err == sql.ErrNoRows { - return fmt.Errorf("no entry for key %q in table %q", base64.StdEncoding.EncodeToString([]byte(encodedKey)), ledgerEntriesTableName) - } else if err != nil { - return err - } - // Unmarshal the existing entry - if err := xdr.SafeUnmarshal([]byte(existing), &entry); err != nil { - return err - } - } - - // Update the expiration - switch entry.Data.Type { - case xdr.LedgerEntryTypeContractData: - entry.Data.ContractData.ExpirationLedgerSeq = expirationLedgerSeq - case xdr.LedgerEntryTypeContractCode: - entry.Data.ContractCode.ExpirationLedgerSeq = expirationLedgerSeq - } - - // Marshal the entry back and stage it - return l.UpsertLedgerEntry(entry) -} - func (l ledgerEntryWriter) UpsertLedgerEntry(entry xdr.LedgerEntry) error { // We can do a little extra validation to ensure the entry and key match, // because the key can be derived from the entry. @@ -264,8 +216,8 @@ func (l *ledgerEntryReadTx) getRawLedgerEntries(keys ...string) (map[string]stri return result, nil } -func GetLedgerEntry(tx LedgerEntryReadTx, includeExpired bool, key xdr.LedgerKey) (bool, xdr.LedgerEntry, error) { - keyEntries, err := tx.GetLedgerEntries(includeExpired, key) +func GetLedgerEntry(tx LedgerEntryReadTx, key xdr.LedgerKey) (bool, xdr.LedgerEntry, error) { + keyEntries, err := tx.GetLedgerEntries(key) if err != nil { return false, xdr.LedgerEntry{}, err } @@ -280,7 +232,7 @@ func GetLedgerEntry(tx LedgerEntryReadTx, includeExpired bool, key xdr.LedgerKey } } -func (l *ledgerEntryReadTx) GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) { +func (l *ledgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) { encodedKeys := make([]string, len(keys)) encodedKeyToKey := make(map[string]xdr.LedgerKey, len(keys)) for i, k := range keys { @@ -307,20 +259,6 @@ func (l *ledgerEntryReadTx) GetLedgerEntries(includeExpired bool, keys ...xdr.Le if err := xdr.SafeUnmarshal([]byte(encodedEntry), &entry); err != nil { return nil, errors.Wrap(err, "cannot decode ledger entry from DB") } - if !includeExpired { - // Disallow access to entries that have expired. Expiration excludes the - // "current" ledger, which we are building. - if expirationLedgerSeq, ok := entry.Data.ExpirationLedgerSeq(); ok { - latestClosedLedger, err := l.GetLatestLedgerSequence() - if err != nil { - return nil, err - } - currentLedger := latestClosedLedger + 1 - if expirationLedgerSeq < xdr.Uint32(currentLedger) { - continue - } - } - } result = append(result, LedgerKeyAndEntry{key, entry}) } diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index e237a1786..9089ebbf1 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -3,7 +3,6 @@ package db import ( "context" "fmt" - "math" "math/rand" "path" "sync" @@ -31,7 +30,7 @@ func getLedgerEntryAndLatestLedgerSequenceWithErr(db *DB, key xdr.LedgerKey) (bo return false, xdr.LedgerEntry{}, 0, err } - present, entry, err := GetLedgerEntry(tx, false, key) + present, entry, err := GetLedgerEntry(tx, key) if err != nil { return false, xdr.LedgerEntry{}, 0, err } @@ -68,16 +67,10 @@ func TestGoldenPath(t *testing.T) { U32: &four, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, }, - ExpirationLedgerSeq: 100, } key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) @@ -89,7 +82,7 @@ func TestGoldenPath(t *testing.T) { assert.Equal(t, ledgerSequence, obtainedLedgerSequence) assert.Equal(t, obtainedEntry.Data.Type, xdr.LedgerEntryTypeContractData) assert.Equal(t, xdr.Hash{0xca, 0xfe}, *obtainedEntry.Data.ContractData.Contract.ContractId) - assert.Equal(t, six, *obtainedEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) obtainedLedgerSequence, err = NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) assert.NoError(t, err) @@ -100,7 +93,7 @@ func TestGoldenPath(t *testing.T) { assert.NoError(t, err) writer = tx.LedgerEntryWriter() eight := xdr.Uint32(8) - entry.Data.ContractData.Body.Data.Val.U32 = &eight + entry.Data.ContractData.Val.U32 = &eight assert.NoError(t, writer.UpsertLedgerEntry(entry)) @@ -110,7 +103,7 @@ func TestGoldenPath(t *testing.T) { present, obtainedEntry, obtainedLedgerSequence = getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, eight, *obtainedEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, eight, *obtainedEntry.Data.ContractData.Val.U32) // Do another round, deleting the ledger entry tx, err = NewReadWriter(db, 150, 15).NewTx(context.Background()) @@ -152,16 +145,10 @@ func TestDeleteNonExistentLedgerEmpty(t *testing.T) { U32: &four, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, }, - ExpirationLedgerSeq: 100, } key, _ := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.DeleteLedgerEntry(key)) @@ -179,253 +166,6 @@ func TestDeleteNonExistentLedgerEmpty(t *testing.T) { assert.Equal(t, ledgerSequence, obtainedLedgerSequence) } -func TestExtendEntry(t *testing.T) { - db := NewTestDB(t) - - // Simulate a ledger which creates a ledger entry, then extends it. - tx, err := NewReadWriter(db, 150, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - four := xdr.Uint32(4) - six := xdr.Uint32(6) - data := xdr.ContractDataEntry{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, - }, - ExpirationLedgerSeq: 24, - } - key, entry := getContractDataLedgerEntry(t, data) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - assert.NoError(t, tx.Commit(uint32(23))) - - // Extend the entry's expiration - tx, err = NewReadWriter(db, 150, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer = tx.LedgerEntryWriter() - assert.NoError(t, writer.ExtendLedgerEntry(key, 32)) - ledgerSequence := uint32(24) - assert.NoError(t, tx.Commit(ledgerSequence)) - - // Make sure that the ledger number was submitted - obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) - assert.NoError(t, err) - assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - - // And that the entry was updated - present, resultEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.True(t, present) - assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, xdr.Uint32(32), resultEntry.Data.ContractData.ExpirationLedgerSeq) -} - -func TestCreateAndImmediatelyExtendEntry(t *testing.T) { - db := NewTestDB(t) - - // Simulate a ledger which creates a ledger entry, then extends it. - tx, err := NewReadWriter(db, 150, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - four := xdr.Uint32(4) - six := xdr.Uint32(6) - data := xdr.ContractDataEntry{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, - }, - ExpirationLedgerSeq: 24, - } - key, entry := getContractDataLedgerEntry(t, data) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - - // Immediately Extend the entry's expiration - assert.NoError(t, writer.ExtendLedgerEntry(key, 32)) - - // Commit everything at once - ledgerSequence := uint32(24) - assert.NoError(t, tx.Commit(ledgerSequence)) - - // Check that the entry was updated - present, resultEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.True(t, present) - assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - - // Check that the extension was applied - assert.Equal(t, xdr.Uint32(32), resultEntry.Data.ContractData.ExpirationLedgerSeq) -} - -func TestExtendNonExistentLedgerEntry(t *testing.T) { - db := NewTestDB(t) - - four := xdr.Uint32(4) - key := xdr.LedgerKey{ - Type: xdr.LedgerEntryTypeContractData, - ContractData: &xdr.LedgerKeyContractData{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, - }, - } - - // Try to extend the entry's expiration - tx, err := NewReadWriter(db, 150, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - err = writer.ExtendLedgerEntry(key, 32) - assert.ErrorContains(t, err, "no entry for key") -} - -func TestGetLedgerEntryHidesExpiredContractDataEntries(t *testing.T) { - db := NewTestDB(t) - - // Check that we get an empty DB error - _, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) - assert.Equal(t, ErrEmptyDB, err) - - // Start filling the DB with a single entry (enforce flushing right away) - tx, err := NewReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - four := xdr.Uint32(4) - six := xdr.Uint32(6) - data := xdr.ContractDataEntry{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, - }, - ExpirationLedgerSeq: 23, - } - key, entry := getContractDataLedgerEntry(t, data) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - assert.NoError(t, tx.Commit(20)) - - for _, c := range []struct { - ledgerSequence uint32 - expected bool - }{ - {21, true}, - {22, true}, - {23, false}, - {24, false}, - } { - // ffwd to the ledger sequence - tx, err := NewReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - // Close the ledger N - assert.NoError(t, tx.Commit(c.ledgerSequence)) - - // Now, ledger N is our latestClosedLedger, so any preflights should act as - // though it is currently ledger N+1 - - // Try to read the entry back, and check it disappears when expected - present, _, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.Equal(t, c.ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, c.expected, present, "ledger sequence %d", c.ledgerSequence) - } -} - -func TestGetLedgerEntryHidesExpiredContractCodeEntries(t *testing.T) { - db := NewTestDB(t) - - // Check that we get an empty DB error - _, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) - assert.Equal(t, ErrEmptyDB, err) - - // Start filling the DB with a single entry (enforce flushing right away) - tx, err := NewReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - source := []byte("some code") - code := xdr.ContractCodeEntry{ - Hash: xdr.Hash{0xca, 0xfe}, - Body: xdr.ContractCodeEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Code: &source, - }, - ExpirationLedgerSeq: 23, - } - key, entry := getContractCodeLedgerEntry(t, code) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - assert.NoError(t, tx.Commit(20)) - - for _, c := range []struct { - ledgerSequence uint32 - expected bool - }{ - {21, true}, - {22, true}, - {23, false}, - {24, false}, - } { - // ffwd to the ledger sequence - tx, err := NewReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - // Close the ledger N - assert.NoError(t, tx.Commit(c.ledgerSequence)) - - // Now, ledger N is our latestClosedLedger, so any preflights should act as - // though it is currently ledger N+1 - - // Try to read the entry back, and check it disappears when expected - present, _, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.Equal(t, c.ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, c.expected, present, "ledger sequence %d", c.ledgerSequence) - } -} - func getContractDataLedgerEntry(t require.TestingT, data xdr.ContractDataEntry) (xdr.LedgerKey, xdr.LedgerEntry) { entry := xdr.LedgerEntry{ LastModifiedLedgerSeq: 1, @@ -436,22 +176,7 @@ func getContractDataLedgerEntry(t require.TestingT, data xdr.ContractDataEntry) Ext: xdr.LedgerEntryExt{}, } var key xdr.LedgerKey - err := key.SetContractData(data.Contract, data.Key, data.Durability, data.Body.BodyType) - require.NoError(t, err) - return key, entry -} - -func getContractCodeLedgerEntry(t require.TestingT, code xdr.ContractCodeEntry) (xdr.LedgerKey, xdr.LedgerEntry) { - entry := xdr.LedgerEntry{ - LastModifiedLedgerSeq: 1, - Data: xdr.LedgerEntryData{ - Type: xdr.LedgerEntryTypeContractCode, - ContractCode: &code, - }, - Ext: xdr.LedgerEntryExt{}, - } - var key xdr.LedgerKey - err := key.SetContractCode(code.Hash) + err := key.SetContractData(data.Contract, data.Key, data.Durability) require.NoError(t, err) return key, entry } @@ -481,17 +206,10 @@ func TestReadTxsDuringWriteTx(t *testing.T) { Type: xdr.ScValTypeScvU32, U32: &four, }, - Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, }, - ExpirationLedgerSeq: math.MaxUint32, } key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) @@ -504,14 +222,14 @@ func TestReadTxsDuringWriteTx(t *testing.T) { _, err = readTx1.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx1, false, key) + present, _, err := GetLedgerEntry(readTx1, key) assert.NoError(t, err) assert.False(t, present) assert.NoError(t, readTx1.Done()) _, err = readTx2.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err = GetLedgerEntry(readTx2, false, key) + present, _, err = GetLedgerEntry(readTx2, key) assert.NoError(t, err) assert.False(t, present) assert.NoError(t, readTx2.Done()) @@ -527,7 +245,7 @@ func TestReadTxsDuringWriteTx(t *testing.T) { present, obtainedEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, six, *obtainedEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) } // Make sure that a write transaction can happen while multiple read transactions are ongoing, @@ -566,16 +284,10 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { U32: &four, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, }, - ExpirationLedgerSeq: math.MaxUint32, } key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) @@ -588,7 +300,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { _, err = readTx.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx, false, key) + present, _, err := GetLedgerEntry(readTx, key) assert.NoError(t, err) assert.False(t, present) } @@ -600,7 +312,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { _, err = readTx.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx, false, key) + present, _, err := GetLedgerEntry(readTx, key) assert.NoError(t, err) assert.False(t, present) } @@ -614,7 +326,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { present, obtainedEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, six, *obtainedEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { assert.NoError(t, readTx.Done()) @@ -643,16 +355,10 @@ func TestConcurrentReadersAndWriter(t *testing.T) { U32: &val, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &val, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &val, }, - ExpirationLedgerSeq: math.MaxUint32, } } rw := NewReadWriter(db, 10, 15) @@ -685,7 +391,6 @@ func TestConcurrentReadersAndWriter(t *testing.T) { U32: &val, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, } for { @@ -703,7 +408,7 @@ func TestConcurrentReadersAndWriter(t *testing.T) { // All entries should be found once the first write commit is done assert.True(t, found) logMessageCh <- fmt.Sprintf("reader %d: for ledger %d", keyVal, ledger) - assert.Equal(t, xdr.Uint32(keyVal), *ledgerEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, xdr.Uint32(keyVal), *ledgerEntry.Data.ContractData.Val.U32) } time.Sleep(time.Duration(rand.Int31n(30)) * time.Millisecond) } @@ -749,16 +454,10 @@ func benchmarkLedgerEntry(b *testing.B, cached bool, includeExpired bool) { U32: &keyUint32, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &keyUint32, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &keyUint32, }, - ExpirationLedgerSeq: math.MaxUint32, } key, entry := getContractDataLedgerEntry(b, data) tx, err := NewReadWriter(db, 150, 15).NewTx(context.Background()) @@ -780,7 +479,7 @@ func benchmarkLedgerEntry(b *testing.B, cached bool, includeExpired bool) { assert.NoError(b, err) for i := 0; i < numQueriesPerOp; i++ { b.StartTimer() - found, _, err := GetLedgerEntry(readTx, includeExpired, key) + found, _, err := GetLedgerEntry(readTx, key) b.StopTimer() assert.NoError(b, err) assert.True(b, found) @@ -808,16 +507,10 @@ func BenchmarkLedgerUpdate(b *testing.B) { U32: &keyUint32, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &keyUint32, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &keyUint32, }, - ExpirationLedgerSeq: math.MaxUint32, } _, entry := getContractDataLedgerEntry(b, data) const numEntriesPerOp = 3500 diff --git a/cmd/soroban-rpc/internal/ingest/ledgerentry.go b/cmd/soroban-rpc/internal/ingest/ledgerentry.go index b10abed69..cc00b884b 100644 --- a/cmd/soroban-rpc/internal/ingest/ledgerentry.go +++ b/cmd/soroban-rpc/internal/ingest/ledgerentry.go @@ -54,25 +54,6 @@ func ingestLedgerEntryChange(writer db.LedgerEntryWriter, change ingest.Change) } return writer.DeleteLedgerEntry(ledgerKey) } else { - ledgerKey, err := xdr.GetLedgerKeyFromData(change.Post.Data) - if err != nil { - return err - } - if isExtension, expirationLedgerSeq := ledgerEntryIsExtension(ledgerKey, change.Post); isExtension { - return writer.ExtendLedgerEntry(ledgerKey, expirationLedgerSeq) - } else { - return writer.UpsertLedgerEntry(*change.Post) - } - } -} - -func ledgerEntryIsExtension(ledgerKey xdr.LedgerKey, entry *xdr.LedgerEntry) (bool, xdr.Uint32) { - switch ledgerKey.Type { - case xdr.LedgerEntryTypeContractCode: - return entry.Data.ContractCode.Body.BodyType == xdr.ContractEntryBodyTypeExpirationExtension, entry.Data.ContractCode.ExpirationLedgerSeq - case xdr.LedgerEntryTypeContractData: - return entry.Data.ContractData.Body.BodyType == xdr.ContractEntryBodyTypeExpirationExtension, entry.Data.ContractData.ExpirationLedgerSeq - default: - return false, 0 + return writer.UpsertLedgerEntry(*change.Post) } } diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go index 1beb15b0a..124ec6a33 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go @@ -42,7 +42,7 @@ func (entryReaderTx ConstantLedgerEntryReaderTx) GetLatestLedgerSequence() (uint return expectedLatestLedgerSequence, nil } -func (entryReaderTx ConstantLedgerEntryReaderTx) GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { +func (entryReaderTx ConstantLedgerEntryReaderTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { return nil, nil } diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go index f4d14d223..b7474e323 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go @@ -78,7 +78,7 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn } ledgerEntryResults := make([]LedgerEntryResult, 0, len(ledgerKeys)) - ledgerKeysAndEntries, err := tx.GetLedgerEntries(false, ledgerKeys...) + ledgerKeysAndEntries, err := tx.GetLedgerEntries(ledgerKeys...) if err != nil { logger.WithError(err).WithField("request", request). Info("could not obtain ledger entryies from storage") diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go index c44fd3f64..4ab908a68 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go @@ -61,7 +61,7 @@ func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntr } } - present, ledgerEntry, err := db.GetLedgerEntry(tx, false, key) + present, ledgerEntry, err := db.GetLedgerEntry(tx, key) if err != nil { logger.WithError(err).WithField("request", request). Info("could not obtain ledger entry from storage") diff --git a/cmd/soroban-rpc/internal/preflight/pool.go b/cmd/soroban-rpc/internal/preflight/pool.go index e23754c16..ff591ddda 100644 --- a/cmd/soroban-rpc/internal/preflight/pool.go +++ b/cmd/soroban-rpc/internal/preflight/pool.go @@ -127,9 +127,9 @@ type metricsLedgerEntryWrapper struct { ledgerEntriesFetched uint32 } -func (m *metricsLedgerEntryWrapper) GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { +func (m *metricsLedgerEntryWrapper) GetLedgerEntries(keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { startTime := time.Now() - entries, err := m.LedgerEntryReadTx.GetLedgerEntries(includeExpired, keys...) + entries, err := m.LedgerEntryReadTx.GetLedgerEntries(keys...) atomic.AddUint64(&m.totalDurationMs, uint64(time.Since(startTime).Milliseconds())) atomic.AddUint32(&m.ledgerEntriesFetched, uint32(len(keys))) return entries, err diff --git a/cmd/soroban-rpc/internal/preflight/preflight.go b/cmd/soroban-rpc/internal/preflight/preflight.go index d7414415c..029a6b7da 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight.go +++ b/cmd/soroban-rpc/internal/preflight/preflight.go @@ -39,14 +39,14 @@ type snapshotSourceHandle struct { // It's used by the Rust preflight code to obtain ledger entries. // //export SnapshotSourceGet -func SnapshotSourceGet(handle C.uintptr_t, cLedgerKey C.xdr_t, includeExpired C.int) C.xdr_t { +func SnapshotSourceGet(handle C.uintptr_t, cLedgerKey C.xdr_t) C.xdr_t { h := cgo.Handle(handle).Value().(snapshotSourceHandle) ledgerKeyXDR := GoXDR(cLedgerKey) var ledgerKey xdr.LedgerKey if err := xdr.SafeUnmarshal(ledgerKeyXDR, &ledgerKey); err != nil { panic(err) } - present, entry, err := db.GetLedgerEntry(h.readTx, includeExpired != 0, ledgerKey) + present, entry, err := db.GetLedgerEntry(h.readTx, ledgerKey) if err != nil { h.logger.WithError(err).Error("SnapshotSourceGet(): GetLedgerEntry() failed") return C.xdr_t{} @@ -181,7 +181,7 @@ func getInvokeHostFunctionPreflight(params PreflightParameters) (Preflight, erro } sourceAccountCXDR := CXDR(sourceAccountXDR) - hasConfig, stateExpirationConfig, err := db.GetLedgerEntry(params.LedgerEntryReadTx, false, xdr.LedgerKey{ + hasConfig, stateExpirationConfig, err := db.GetLedgerEntry(params.LedgerEntryReadTx, xdr.LedgerKey{ Type: xdr.LedgerEntryTypeConfigSetting, ConfigSetting: &xdr.LedgerKeyConfigSetting{ ConfigSettingId: xdr.ConfigSettingIdConfigSettingStateExpiration, @@ -210,7 +210,6 @@ func getInvokeHostFunctionPreflight(params PreflightParameters) (Preflight, erro min_temp_entry_expiration: C.uint(stateExpiration.MinTempEntryExpiration), min_persistent_entry_expiration: C.uint(stateExpiration.MinPersistentEntryExpiration), max_entry_expiration: C.uint(stateExpiration.MaxEntryExpiration), - auto_bump_ledgers: C.uint(stateExpiration.AutoBumpLedgers), } handle := cgo.NewHandle(snapshotSourceHandle{params.LedgerEntryReadTx, params.Logger}) diff --git a/cmd/soroban-rpc/internal/preflight/preflight_test.go b/cmd/soroban-rpc/internal/preflight/preflight_test.go index 9d4ea3c26..b3215f49d 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight_test.go +++ b/cmd/soroban-rpc/internal/preflight/preflight_test.go @@ -2,6 +2,7 @@ package preflight import ( "context" + "crypto/sha256" "os" "path" "runtime" @@ -31,7 +32,7 @@ var contractCostParams = func() *xdr.ContractCostParams { return &result }() -var mockLedgerEntries = []xdr.LedgerEntry{ +var mockLedgerEntriesWithoutExpirations = []xdr.LedgerEntry{ { LastModifiedLedgerSeq: 1, Data: xdr.LedgerEntryData{ @@ -44,22 +45,15 @@ var mockLedgerEntries = []xdr.LedgerEntry{ Key: xdr.ScVal{ Type: xdr.ScValTypeScvLedgerKeyContractInstance, }, - Durability: xdr.ContractDataDurabilityPersistent, - ExpirationLedgerSeq: 100000, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Flags: 0, - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvContractInstance, - Instance: &xdr.ScContractInstance{ - Executable: xdr.ContractExecutable{ - Type: xdr.ContractExecutableTypeContractExecutableWasm, - WasmHash: &mockContractHash, - }, - Storage: nil, - }, + Durability: xdr.ContractDataDurabilityPersistent, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvContractInstance, + Instance: &xdr.ScContractInstance{ + Executable: xdr.ContractExecutable{ + Type: xdr.ContractExecutableTypeContractExecutableWasm, + WasmHash: &mockContractHash, }, + Storage: nil, }, }, }, @@ -71,11 +65,7 @@ var mockLedgerEntries = []xdr.LedgerEntry{ Type: xdr.LedgerEntryTypeContractCode, ContractCode: &xdr.ContractCodeEntry{ Hash: mockContractHash, - Body: xdr.ContractCodeEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Code: &helloWorldContract, - }, - ExpirationLedgerSeq: 20000, + Code: helloWorldContract, }, }, }, @@ -169,7 +159,6 @@ var mockLedgerEntries = []xdr.LedgerEntry{ MaxEntryExpiration: 100, MinTempEntryExpiration: 100, MinPersistentEntryExpiration: 100, - AutoBumpLedgers: 100, PersistentRentRateDenominator: 100, TempRentRateDenominator: 100, MaxEntriesToExpire: 100, @@ -203,6 +192,38 @@ var mockLedgerEntries = []xdr.LedgerEntry{ }, } +// Adds expiration entries to mockLedgerEntriesWithoutExpirations +var mockLedgerEntries = func() []xdr.LedgerEntry { + result := make([]xdr.LedgerEntry, 0, len(mockLedgerEntriesWithoutExpirations)) + for _, entry := range mockLedgerEntriesWithoutExpirations { + result = append(result, entry) + + if entry.Data.Type == xdr.LedgerEntryTypeContractData || entry.Data.Type == xdr.LedgerEntryTypeContractCode { + key, err := entry.LedgerKey() + if err != nil { + panic(err) + } + bin, err := key.MarshalBinary() + if err != nil { + panic(err) + } + expirationEntry := xdr.LedgerEntry{ + LastModifiedLedgerSeq: entry.LastModifiedLedgerSeq, + Data: xdr.LedgerEntryData{ + Type: xdr.LedgerEntryTypeExpiration, + Expiration: &xdr.ExpirationEntry{ + KeyHash: sha256.Sum256(bin), + // Make sure it doesn't expire + ExpirationLedgerSeq: 1000, + }, + }, + } + result = append(result, expirationEntry) + } + } + return result +}() + var helloWorldContract = func() []byte { _, filename, _, _ := runtime.Caller(0) testDirName := path.Dir(filename) @@ -216,7 +237,7 @@ var helloWorldContract = func() []byte { type inMemoryLedgerEntryReadTx map[string]xdr.LedgerEntry -func (m inMemoryLedgerEntryReadTx) GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { +func (m inMemoryLedgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { result := make([]db.LedgerKeyAndEntry, 0, len(keys)) for _, key := range keys { serializedKey, err := key.MarshalBinaryBase64() @@ -336,8 +357,9 @@ func getPreflightParameters(t testing.TB, dbConfig *preflightParametersDBConfig) func TestGetPreflight(t *testing.T) { // in-memory params := getPreflightParameters(t, nil) - _, err := GetPreflight(context.Background(), params) + result, err := GetPreflight(context.Background(), params) require.NoError(t, err) + require.Empty(t, result.Error) require.NoError(t, params.LedgerEntryReadTx.Done()) // using a restarted db with caching and @@ -347,8 +369,9 @@ func TestGetPreflight(t *testing.T) { disableCache: false, } params = getPreflightParameters(t, dbConfig) - _, err = GetPreflight(context.Background(), params) + result, err = GetPreflight(context.Background(), params) require.NoError(t, err) + require.Empty(t, result.Error) require.NoError(t, params.LedgerEntryReadTx.Done()) require.NoError(t, dbConfig.dbInstance.Close()) } @@ -376,9 +399,10 @@ func benchmark(b *testing.B, config benchmarkConfig) { for i := 0; i < b.N; i++ { params := getPreflightParameters(b, dbConfig) b.StartTimer() - _, err := GetPreflight(context.Background(), params) + result, err := GetPreflight(context.Background(), params) b.StopTimer() require.NoError(b, err) + require.Empty(b, result.Error) require.NoError(b, params.LedgerEntryReadTx.Done()) } if dbConfig != nil { diff --git a/cmd/soroban-rpc/internal/test/docker-compose.yml b/cmd/soroban-rpc/internal/test/docker-compose.yml index 27d275916..d983857d9 100644 --- a/cmd/soroban-rpc/internal/test/docker-compose.yml +++ b/cmd/soroban-rpc/internal/test/docker-compose.yml @@ -15,7 +15,7 @@ services: # Note: Please keep the image pinned to an immutable tag matching the Captive Core version. # This avoids implicit updates which break compatibility between # the Core container and captive core. - image: ${CORE_IMAGE:-stellar/unsafe-stellar-core-next:19.13.1-1458.431e4e324.focal-soroban-settings-override} + image: ${CORE_IMAGE:-stellar/unsafe-stellar-core-next:19.13.1-1462.22b9bb384.focal-vnext} depends_on: - core-postgres restart: on-failure diff --git a/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go b/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go index cf0d3d97d..d8154b3db 100644 --- a/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go +++ b/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go @@ -38,7 +38,6 @@ func TestGetLedgerEntriesNotFound(t *testing.T) { Type: xdr.ScValTypeScvLedgerKeyContractInstance, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, }) require.NoError(t, err) @@ -135,7 +134,6 @@ func TestGetLedgerEntriesSucceeds(t *testing.T) { Type: xdr.ScValTypeScvLedgerKeyContractInstance, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, }) require.NoError(t, err) @@ -155,6 +153,6 @@ func TestGetLedgerEntriesSucceeds(t *testing.T) { var firstEntry xdr.LedgerEntryData require.NoError(t, xdr.SafeUnmarshalBase64(result.Entries[0].XDR, &firstEntry)) - require.Equal(t, testContract, *firstEntry.MustContractCode().Body.Code) + require.Equal(t, testContract, firstEntry.MustContractCode().Code) require.Equal(t, contractKeyB64, result.Entries[0].Key) } diff --git a/cmd/soroban-rpc/internal/test/get_ledger_entry_test.go b/cmd/soroban-rpc/internal/test/get_ledger_entry_test.go index 85a3b76d7..4b8e17572 100644 --- a/cmd/soroban-rpc/internal/test/get_ledger_entry_test.go +++ b/cmd/soroban-rpc/internal/test/get_ledger_entry_test.go @@ -37,7 +37,6 @@ func TestGetLedgerEntryNotFound(t *testing.T) { Type: xdr.ScValTypeScvLedgerKeyContractInstance, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, }) require.NoError(t, err) @@ -111,5 +110,5 @@ func TestGetLedgerEntrySucceeds(t *testing.T) { assert.GreaterOrEqual(t, result.LatestLedger, result.LastModifiedLedger) var entry xdr.LedgerEntryData assert.NoError(t, xdr.SafeUnmarshalBase64(result.XDR, &entry)) - assert.Equal(t, testContract, *entry.MustContractCode().Body.Code) + assert.Equal(t, testContract, entry.MustContractCode().Code) } diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index f73c66702..1ea57f2c8 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -190,6 +190,8 @@ func preflightTransactionParamsLocally(t *testing.T, params txnbuild.Transaction func preflightTransactionParams(t *testing.T, client *jrpc2.Client, params txnbuild.TransactionParams) txnbuild.TransactionParams { response := simulateTransactionFromTxParams(t, client, params) + // The preamble should be zero except for the special restore case + assert.Zero(t, response.RestorePreamble) return preflightTransactionParamsLocally(t, params, response) } @@ -233,15 +235,14 @@ func TestSimulateTransactionSucceeds(t *testing.T) { { Type: xdr.LedgerEntryTypeContractCode, ContractCode: &xdr.LedgerKeyContractCode{ - Hash: xdr.Hash(testContractId), - BodyType: xdr.ContractEntryBodyTypeDataEntry, + Hash: xdr.Hash(testContractId), }, }, }, }, - Instructions: 77283, - ReadBytes: 40, - WriteBytes: 112, + Instructions: 79653, + ReadBytes: 72, + WriteBytes: 100, }, RefundableFee: 20045, } @@ -488,7 +489,7 @@ func TestSimulateInvokeContractTransactionSucceeds(t *testing.T) { require.Contains(t, metrics, "soroban_rpc_json_rpc_request_duration_seconds_count{endpoint=\"simulateTransaction\",status=\"ok\"} 3") require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_get_duration_seconds_count{status=\"ok\",type=\"db\"} 3") require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_get_duration_seconds_count{status=\"ok\",type=\"all\"} 3") - require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_entries_fetched_sum 55") + require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_entries_fetched_sum 67") } func TestSimulateTransactionError(t *testing.T) { @@ -676,7 +677,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { assert.NoError(t, err) sendSuccessfulTransaction(t, client, sourceAccount, tx) - // get the counter ledger entry + // get the counter ledger entry expiration contractIDHash := xdr.Hash(contractID) counterSym := xdr.ScSymbol("COUNTER") key := xdr.LedgerKey{ @@ -691,10 +692,20 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { Sym: &counterSym, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, } - keyB64, err := xdr.MarshalBase64(key) + + binKey, err := key.MarshalBinary() + assert.NoError(t, err) + + expirationKey := xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeExpiration, + Expiration: &xdr.LedgerKeyExpiration{ + KeyHash: sha256.Sum256(binKey), + }, + } + + keyB64, err := xdr.MarshalBase64(expirationKey) require.NoError(t, err) getLedgerEntryrequest := methods.GetLedgerEntryRequest{ Key: keyB64, @@ -704,9 +715,11 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { assert.NoError(t, err) var entry xdr.LedgerEntryData assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - initialExpirationSeq, ok := entry.ExpirationLedgerSeq() - assert.True(t, ok) + assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + initialExpirationSeq := entry.Expiration.ExpirationLedgerSeq + + // bump the initial expiration params = preflightTransactionParams(t, client, txnbuild.TransactionParams{ SourceAccount: &account, IncrementSequenceNum: true, @@ -737,9 +750,8 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) assert.NoError(t, err) assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - newExpirationSeq, ok := entry.ExpirationLedgerSeq() - assert.True(t, ok) - + assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + newExpirationSeq := entry.Expiration.ExpirationLedgerSeq assert.Greater(t, newExpirationSeq, initialExpirationSeq) // Wait until it expires @@ -747,13 +759,17 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { expired := false for i := 0; i < 50; i++ { err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) - if err != nil { + assert.NoError(t, err) + assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) + assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + // See https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#expiration-ledger + currentLedger := getLedgerEntryResult.LatestLedger + 1 + if xdr.Uint32(currentLedger) > entry.Expiration.ExpirationLedgerSeq { expired = true t.Logf("ledger entry expired") break } - assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - t.Log("waiting for ledger entry to expire at ledger", entry.MustContractData().ExpirationLedgerSeq) + t.Log("waiting for ledger entry to expire at ledger", entry.Expiration.ExpirationLedgerSeq) time.Sleep(time.Second) } require.True(t, expired) diff --git a/cmd/soroban-rpc/lib/preflight.h b/cmd/soroban-rpc/lib/preflight.h index ad958cd92..fc01d9f45 100644 --- a/cmd/soroban-rpc/lib/preflight.h +++ b/cmd/soroban-rpc/lib/preflight.h @@ -12,7 +12,6 @@ typedef struct ledger_info_t { uint32_t min_temp_entry_expiration; uint32_t min_persistent_entry_expiration; uint32_t max_entry_expiration; - uint32_t auto_bump_ledgers; } ledger_info_t; typedef struct xdr_t { @@ -52,7 +51,7 @@ preflight_result_t *preflight_footprint_expiration_op(uintptr_t handle, // Go // LedgerKey XDR to LedgerEntry XDR -extern xdr_t SnapshotSourceGet(uintptr_t handle, xdr_t ledger_key, int include_expired); +extern xdr_t SnapshotSourceGet(uintptr_t handle, xdr_t ledger_key); void free_preflight_result(preflight_result_t *result); diff --git a/cmd/soroban-rpc/lib/preflight/src/fees.rs b/cmd/soroban-rpc/lib/preflight/src/fees.rs index b016ba5a8..bc06ca313 100644 --- a/cmd/soroban-rpc/lib/preflight/src/fees.rs +++ b/cmd/soroban-rpc/lib/preflight/src/fees.rs @@ -1,7 +1,9 @@ use anyhow::{bail, ensure, Context, Error, Result}; use ledger_storage::LedgerStorage; use soroban_env_host::budget::Budget; -use soroban_env_host::e2e_invoke::{extract_rent_changes, get_ledger_changes, LedgerEntryChange}; +use soroban_env_host::e2e_invoke::{ + extract_rent_changes, get_ledger_changes, ExpirationEntryMap, LedgerEntryChange, +}; use soroban_env_host::fees::{ compute_rent_fee, compute_transaction_resource_fee, compute_write_fee_per_1kb, FeeConfiguration, LedgerEntryRentChange, RentFeeConfiguration, TransactionResources, @@ -21,6 +23,10 @@ use state_expiration::{get_restored_ledger_sequence, ExpirableLedgerEntry}; use std::cmp::max; use std::convert::{TryFrom, TryInto}; +/// Estimate for any `ExpirationEntry` ledger entry, consisting of a 32-byte +/// hash of the corresponding entry and 4 bytes for expiration ledger. +const EXPIRATION_ENTRY_SIZE: u32 = 32 + 4; + pub(crate) fn compute_host_function_transaction_data_and_min_fee( op: &InvokeHostFunctionOp, pre_storage: &LedgerStorage, @@ -31,7 +37,10 @@ pub(crate) fn compute_host_function_transaction_data_and_min_fee( bucket_list_size: u64, current_ledger_seq: u32, ) -> Result<(SorobanTransactionData, i64)> { - let ledger_changes = get_ledger_changes(budget, post_storage, pre_storage)?; + // TODO: is this OK? + let init_expiration_entries = ExpirationEntryMap::new(); + let ledger_changes = + get_ledger_changes(budget, post_storage, pre_storage, init_expiration_entries)?; let soroban_resources = calculate_host_function_soroban_resources(&ledger_changes, &post_storage.footprint, budget) .context("cannot compute host function resources")?; @@ -132,7 +141,15 @@ fn calculate_host_function_soroban_resources( .context("cannot convert storage footprint to ledger footprint")?; let read_bytes: u32 = ledger_changes .iter() - .map(|c| c.encoded_key.len() as u32 + c.old_entry_size_bytes) + .map( + |c| { + let mut size = c.encoded_key.len() as u32 + c.old_entry_size_bytes; + if c.expiration_change.is_some() { + size += EXPIRATION_ENTRY_SIZE; + } + size + }, //size + ) .sum(); let write_bytes: u32 = ledger_changes @@ -224,6 +241,17 @@ fn get_fee_configurations( Ok((fee_configuration, rent_fee_configuration)) } +// Calculate the implicit ExpirationEntry bytes that will be read for expirable LedgerEntries +fn calculate_expiration_entry_bytes(ledger_entries: &Vec) -> Result { + Ok(ledger_entries + .iter() + .map(|lk| match lk { + LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => EXPIRATION_ENTRY_SIZE, + _ => 0, + }) + .sum()) +} + fn calculate_unmodified_ledger_entry_bytes( ledger_entries: &Vec, pre_storage: &LedgerStorage, @@ -313,16 +341,20 @@ pub(crate) fn compute_bump_footprint_exp_transaction_data_and_min_fee( current_ledger_seq, ) .context("cannot compute bump rent changes")?; - let read_bytes = calculate_unmodified_ledger_entry_bytes( + + let expiration_bytes: u32 = calculate_expiration_entry_bytes(footprint.read_only.as_vec())?; + + let unmodified_entry_bytes = calculate_unmodified_ledger_entry_bytes( footprint.read_only.as_vec(), ledger_storage, false, ) .context("cannot calculate read_bytes resource")?; + let soroban_resources = SorobanResources { footprint, instructions: 0, - read_bytes, + read_bytes: unmodified_entry_bytes + expiration_bytes, write_bytes: 0, }; let transaction_size_bytes = estimate_max_transaction_size_for_operation( @@ -361,12 +393,13 @@ fn compute_bump_footprint_rent_changes( let mut rent_changes: Vec = Vec::with_capacity(footprint.read_only.len()); for key in (&footprint).read_only.as_vec() { - let unmodified_entry = ledger_storage + let unmodified_entry_and_expiration = ledger_storage .get(key, false) .with_context(|| format!("cannot find bump footprint ledger entry with key {key:?}"))?; - let size = (key.to_xdr()?.len() + unmodified_entry.to_xdr()?.len()) as u32; - let expirable_entry: Box = - (&unmodified_entry).try_into().map_err(|e: String| { + let size = (key.to_xdr()?.len() + unmodified_entry_and_expiration.0.to_xdr()?.len()) as u32; + let expirable_entry: Box = (&unmodified_entry_and_expiration) + .try_into() + .map_err(|e: String| { Error::msg(e.clone()).context("incorrect ledger entry type in footprint") })?; let new_expiration_ledger = current_ledger_seq + ledgers_to_expire; @@ -404,6 +437,8 @@ pub(crate) fn compute_restore_footprint_transaction_data_and_min_fee( current_ledger_seq, ) .context("cannot compute restore rent changes")?; + + let expiration_bytes: u32 = calculate_expiration_entry_bytes(footprint.read_write.as_vec())?; let write_bytes = calculate_unmodified_ledger_entry_bytes( footprint.read_write.as_vec(), ledger_storage, @@ -413,7 +448,7 @@ pub(crate) fn compute_restore_footprint_transaction_data_and_min_fee( let soroban_resources = SorobanResources { footprint, instructions: 0, - read_bytes: write_bytes, + read_bytes: write_bytes + expiration_bytes, write_bytes, }; let entry_count = u32::try_from(soroban_resources.footprint.read_write.as_vec().len())?; @@ -452,12 +487,13 @@ fn compute_restore_footprint_rent_changes( let mut rent_changes: Vec = Vec::with_capacity(footprint.read_write.len()); for key in footprint.read_write.as_vec() { - let unmodified_entry = ledger_storage.get(key, true).with_context(|| { + let unmodified_entry_and_expiration = ledger_storage.get(key, true).with_context(|| { format!("cannot find restore footprint ledger entry with key {key:?}") })?; - let size = (key.to_xdr()?.len() + unmodified_entry.to_xdr()?.len()) as u32; - let expirable_entry: Box = - (&unmodified_entry).try_into().map_err(|e: String| { + let size = (key.to_xdr()?.len() + unmodified_entry_and_expiration.0.to_xdr()?.len()) as u32; + let expirable_entry: Box = (&unmodified_entry_and_expiration) + .try_into() + .map_err(|e: String| { Error::msg(e.clone()).context("incorrect ledger entry type in footprint") })?; ensure!( diff --git a/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs b/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs index 4159dda2e..2ac3862b8 100644 --- a/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs +++ b/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs @@ -1,11 +1,13 @@ +use sha2::Digest; use soroban_env_host::storage::SnapshotSource; use soroban_env_host::xdr::ContractDataDurability::Persistent; use soroban_env_host::xdr::{ - ConfigSettingEntry, ConfigSettingId, Error as XdrError, LedgerEntry, LedgerEntryData, - LedgerKey, LedgerKeyConfigSetting, ReadXdr, ScError, ScErrorCode, WriteXdr, + ConfigSettingEntry, ConfigSettingId, Error as XdrError, ExpirationEntry, Hash, LedgerEntry, + LedgerEntryData, LedgerKey, LedgerKeyConfigSetting, LedgerKeyExpiration, ReadXdr, ScError, + ScErrorCode, WriteXdr, }; use soroban_env_host::HostError; -use state_expiration::{restore_ledger_entry, ExpirableLedgerEntry}; +use state_expiration::{get_restored_ledger_sequence, has_expired, ExpirableLedgerEntry}; use std::cell::RefCell; use std::collections::HashSet; use std::convert::TryInto; @@ -19,11 +21,7 @@ extern "C" { // Free Strings returned from Go functions fn FreeGoXDR(xdr: CXDR); // LedgerKey XDR in base64 string to LedgerEntry XDR in base64 string - fn SnapshotSourceGet( - handle: libc::uintptr_t, - ledger_key: CXDR, - include_expired: libc::c_int, - ) -> CXDR; + fn SnapshotSourceGet(handle: libc::uintptr_t, ledger_key: CXDR) -> CXDR; } #[derive(thiserror::Error, Debug)] @@ -38,6 +36,8 @@ pub(crate) enum Error { Utf8Error(#[from] Utf8Error), #[error("unexpected config ledger entry for setting_id {setting_id}")] UnexpectedConfigLedgerEntry { setting_id: String }, + #[error("unexpected ledger entry type ({ledger_entry_type}) for expiration ledger key")] + UnexpectedLedgerEntryTypeForExpirationKey { ledger_entry_type: String }, } impl From for HostError { @@ -51,54 +51,54 @@ impl From for HostError { } struct EntryRestoreTracker { - current_ledger_seq: u32, min_persistent_entry_expiration: u32, // RefCell is needed to mutate the hashset inside SnapshotSource::get(), which is an immutable method ledger_keys_requiring_restore: RefCell>, } impl EntryRestoreTracker { - pub(crate) fn track_and_restore(&self, key: &LedgerKey, entry: &mut LedgerEntry) { - if self.track(key, entry) { - restore_ledger_entry( - entry, - self.current_ledger_seq, - self.min_persistent_entry_expiration, - ); - } - } - - pub(crate) fn track(&self, key: &LedgerKey, entry: &LedgerEntry) -> bool { - let expirable_entry: Box = match entry.try_into() { + // Tracks ledger entries which need to be restored and returns its expiration as it was restored + pub(crate) fn track_and_restore( + &self, + current_ledger_sequence: u32, + key: &LedgerKey, + entry_and_expiration: &(LedgerEntry, Option), + ) -> Option { + let expirable_entry: Box = match entry_and_expiration.try_into() { Ok(e) => e, Err(_) => { // Nothing to track, the entry isn't expirable - return false; + return None; } }; if expirable_entry.durability() != Persistent - || !expirable_entry.has_expired(self.current_ledger_seq) + || !expirable_entry.has_expired(current_ledger_sequence) { // Nothing to track, the entry isn't persistent (and thus not restorable) or // it hasn't expired - return false; + return Some(expirable_entry.expiration_ledger_seq()); } self.ledger_keys_requiring_restore .borrow_mut() .insert(key.clone()); - true + Some(get_restored_ledger_sequence( + current_ledger_sequence, + self.min_persistent_entry_expiration, + )) } } pub(crate) struct LedgerStorage { golang_handle: libc::uintptr_t, + current_ledger_sequence: u32, restore_tracker: Option, } impl LedgerStorage { - pub(crate) fn new(golang_handle: libc::uintptr_t) -> Self { + pub(crate) fn new(golang_handle: libc::uintptr_t, current_ledger_sequence: u32) -> Self { LedgerStorage { golang_handle, + current_ledger_sequence, restore_tracker: None, } } @@ -110,6 +110,7 @@ impl LedgerStorage { // First, we initialize it without the tracker, to get the minimum restore ledger from the network let mut ledger_storage = LedgerStorage { golang_handle, + current_ledger_sequence, restore_tracker: None, }; let setting_id = ConfigSettingId::StateExpiration; @@ -122,27 +123,19 @@ impl LedgerStorage { }; // Now that we have the state expiration config, we can build the tracker ledger_storage.restore_tracker = Some(EntryRestoreTracker { - current_ledger_seq: current_ledger_sequence, ledger_keys_requiring_restore: RefCell::new(HashSet::new()), min_persistent_entry_expiration: state_expiration.min_persistent_entry_expiration, }); Ok(ledger_storage) } - pub(crate) fn get(&self, key: &LedgerKey, include_expired: bool) -> Result { - let xdr = self.get_xdr(key, include_expired)?; - let entry = LedgerEntry::from_xdr(xdr)?; - Ok(entry) - } - - pub(crate) fn get_xdr(&self, key: &LedgerKey, include_expired: bool) -> Result, Error> { - let mut key_xdr = key.to_xdr()?; + // Get the XDR, regardless of expiration + fn get_xdr_internal(&self, key_xdr: &mut Vec) -> Result, Error> { let key_c_xdr = CXDR { xdr: key_xdr.as_mut_ptr(), len: key_xdr.len(), }; - let res = - unsafe { SnapshotSourceGet(self.golang_handle, key_c_xdr, include_expired.into()) }; + let res = unsafe { SnapshotSourceGet(self.golang_handle, key_c_xdr) }; if res.xdr.is_null() { return Err(Error::NotFound); } @@ -151,6 +144,58 @@ impl LedgerStorage { Ok(v) } + pub(crate) fn get( + &self, + key: &LedgerKey, + include_expired: bool, + ) -> Result<(LedgerEntry, Option), Error> { + let mut key_xdr = key.to_xdr()?; + let xdr = self.get_xdr_internal(&mut key_xdr)?; + + let expiration_seq = match key { + // TODO: it would probably be more efficient to do all of this in the Go side + // (e.g. it would allow us to query multiple entries at once) + LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => { + let key_hash: [u8; 32] = sha2::Sha256::digest(key_xdr).into(); + let expiration_key = LedgerKey::Expiration(LedgerKeyExpiration { + key_hash: Hash(key_hash), + }); + let mut expiration_key_xdr = expiration_key.to_xdr()?; + let expiration_entry_xdr = self.get_xdr_internal(&mut expiration_key_xdr)?; + let expiration_entry = LedgerEntry::from_xdr(expiration_entry_xdr)?; + if let LedgerEntryData::Expiration(ExpirationEntry { + expiration_ledger_seq, + .. + }) = expiration_entry.data + { + Some(expiration_ledger_seq) + } else { + return Err(Error::UnexpectedLedgerEntryTypeForExpirationKey { + ledger_entry_type: expiration_entry.data.name().to_string(), + }); + } + } + _ => None, + }; + + if !include_expired + && expiration_seq.is_some() + && has_expired(expiration_seq.unwrap(), self.current_ledger_sequence) + { + return Err(Error::NotFound); + } + + let entry = LedgerEntry::from_xdr(xdr)?; + Ok((entry, expiration_seq)) + } + + pub(crate) fn get_xdr(&self, key: &LedgerKey, include_expired: bool) -> Result, Error> { + // TODO: this can be optimized since for entry types other than ContractCode/ContractData, + // they don't need to be deserialized and serialized again + let (entry, _) = self.get(key, include_expired)?; + Ok(entry.to_xdr()?) + } + pub(crate) fn get_configuration_setting( &self, setting_id: ConfigSettingId, @@ -159,10 +204,13 @@ impl LedgerStorage { config_setting_id: setting_id, }); match self.get(&key, false)? { - LedgerEntry { - data: LedgerEntryData::ConfigSetting(cs), - .. - } => Ok(cs), + ( + LedgerEntry { + data: LedgerEntryData::ConfigSetting(cs), + .. + }, + _, + ) => Ok(cs), _ => Err(Error::UnexpectedConfigLedgerEntry { setting_id: setting_id.name().to_string(), }), @@ -178,25 +226,28 @@ impl LedgerStorage { } impl SnapshotSource for LedgerStorage { - fn get(&self, key: &Rc) -> Result, HostError> { - let mut entry = ::get(self, key, self.restore_tracker.is_some())?; + fn get(&self, key: &Rc) -> Result<(Rc, Option), HostError> { + let mut entry_and_expiration = + ::get(self, key, self.restore_tracker.is_some())?; if let Some(ref tracker) = self.restore_tracker { - // If the entry expired, we modify it to make it seem like it was restored - tracker.track_and_restore(key, &mut entry); + // If the entry expired, we modify the expiration to make it seem like it was restored + entry_and_expiration.1 = + tracker.track_and_restore(self.current_ledger_sequence, key, &entry_and_expiration); } - Ok(entry.into()) + Ok((entry_and_expiration.0.into(), entry_and_expiration.1)) } fn has(&self, key: &Rc) -> Result { - let entry = match ::get(self, key, self.restore_tracker.is_some()) { - Err(e) => match e { - Error::NotFound => return Ok(false), - _ => return Err(HostError::from(e)), - }, - Ok(le) => le, - }; + let entry_and_expiration = + match ::get(self, key, self.restore_tracker.is_some()) { + Err(e) => match e { + Error::NotFound => return Ok(false), + _ => return Err(HostError::from(e)), + }, + Ok(le) => le, + }; if let Some(ref tracker) = self.restore_tracker { - tracker.track(key, &entry); + _ = tracker.track_and_restore(self.current_ledger_sequence, key, &entry_and_expiration); } Ok(true) } diff --git a/cmd/soroban-rpc/lib/preflight/src/lib.rs b/cmd/soroban-rpc/lib/preflight/src/lib.rs index 5f913f044..4df90fb64 100644 --- a/cmd/soroban-rpc/lib/preflight/src/lib.rs +++ b/cmd/soroban-rpc/lib/preflight/src/lib.rs @@ -33,7 +33,6 @@ pub struct CLedgerInfo { pub min_temp_entry_expiration: u32, pub min_persistent_entry_expiration: u32, pub max_entry_expiration: u32, - pub autobump_ledgers: u32, } impl From for LedgerInfo { @@ -48,7 +47,6 @@ impl From for LedgerInfo { min_temp_entry_expiration: c.min_temp_entry_expiration, min_persistent_entry_expiration: c.min_persistent_entry_expiration, max_entry_expiration: c.max_entry_expiration, - autobump_ledgers: c.autobump_ledgers, } } } @@ -196,7 +194,7 @@ fn preflight_footprint_expiration_op_or_maybe_panic( ) -> Result { let op_body = OperationBody::from_xdr(from_c_xdr(op_body)).unwrap(); let footprint = LedgerFootprint::from_xdr(from_c_xdr(footprint)).unwrap(); - let ledger_storage = &LedgerStorage::new(handle); + let ledger_storage = &LedgerStorage::new(handle, current_ledger_seq); let result = preflight::preflight_footprint_expiration_op( ledger_storage, bucket_list_size, diff --git a/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs b/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs index 9efd330c8..d0c9bbff1 100644 --- a/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs +++ b/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs @@ -8,62 +8,63 @@ pub(crate) trait ExpirableLedgerEntry { fn durability(&self) -> ContractDataDurability; fn expiration_ledger_seq(&self) -> u32; fn has_expired(&self, current_ledger_seq: u32) -> bool { - current_ledger_seq > self.expiration_ledger_seq() + has_expired(self.expiration_ledger_seq(), current_ledger_seq) } } -impl ExpirableLedgerEntry for &ContractCodeEntry { +impl ExpirableLedgerEntry for (&ContractCodeEntry, u32) { fn durability(&self) -> ContractDataDurability { Persistent } fn expiration_ledger_seq(&self) -> u32 { - self.expiration_ledger_seq + self.1 } } -impl ExpirableLedgerEntry for &ContractDataEntry { +impl ExpirableLedgerEntry for (&ContractDataEntry, u32) { fn durability(&self) -> ContractDataDurability { - self.durability + self.0.durability } fn expiration_ledger_seq(&self) -> u32 { - self.expiration_ledger_seq + self.1 } } -impl<'a> TryInto> for &'a LedgerEntry { +// Convert a ledger entry and its expiration into an ExpirableLedgerEntry +impl<'a> TryInto> for &'a (LedgerEntry, Option) { type Error = String; fn try_into(self) -> Result, Self::Error> { - match &self.data { - LedgerEntryData::ContractData(d) => Ok(Box::new(d)), - LedgerEntryData::ContractCode(c) => Ok(Box::new(c)), + match (&self.0.data, self.1) { + (LedgerEntryData::ContractData(d), Some(expiration_seq)) => { + Ok(Box::new((d, expiration_seq))) + } + (LedgerEntryData::ContractCode(c), Some(expiration_seq)) => { + Ok(Box::new((c, expiration_seq))) + } + (LedgerEntryData::ContractData(_) | LedgerEntryData::ContractCode(_), _) => { + Err(format!( + "missing expiration for expirable ledger entry ({})", + self.0.data.name() + )) + } _ => Err(format!( "ledger entry type ({}) is not expirable", - self.data.name() + self.0.data.name() )), } } } +pub(crate) fn has_expired(expiration_ledger_seq: u32, current_ledger_seq: u32) -> bool { + current_ledger_seq > expiration_ledger_seq +} + pub(crate) fn get_restored_ledger_sequence( current_ledger_seq: u32, min_persistent_entry_expiration: u32, ) -> u32 { return current_ledger_seq + min_persistent_entry_expiration - 1; } - -pub(crate) fn restore_ledger_entry( - ledger_entry: &mut LedgerEntry, - current_ledger_seq: u32, - min_persistent_entry_expiration: u32, -) { - let new_ledger_seq = - get_restored_ledger_sequence(current_ledger_seq, min_persistent_entry_expiration); - match &mut ledger_entry.data { - LedgerEntryData::ContractData(d) => d.expiration_ledger_seq = new_ledger_seq, - LedgerEntryData::ContractCode(c) => c.expiration_ledger_seq = new_ledger_seq, - _ => (), - } -} diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index f528ccb35..ea17a2183 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -833,7 +833,7 @@ Decode XDR * `--type ` — XDR type to decode to - Possible values: `Value`, `ScpBallot`, `ScpStatementType`, `ScpNomination`, `ScpStatement`, `ScpStatementPledges`, `ScpStatementPrepare`, `ScpStatementConfirm`, `ScpStatementExternalize`, `ScpEnvelope`, `ScpQuorumSet`, `ConfigSettingContractExecutionLanesV0`, `ConfigSettingContractComputeV0`, `ConfigSettingContractLedgerCostV0`, `ConfigSettingContractHistoricalDataV0`, `ConfigSettingContractEventsV0`, `ConfigSettingContractBandwidthV0`, `ContractCostType`, `ContractCostParamEntry`, `StateExpirationSettings`, `EvictionIterator`, `ContractCostParams`, `ConfigSettingId`, `ConfigSettingEntry`, `ScEnvMetaKind`, `ScEnvMetaEntry`, `ScMetaV0`, `ScMetaKind`, `ScMetaEntry`, `ScSpecType`, `ScSpecTypeOption`, `ScSpecTypeResult`, `ScSpecTypeVec`, `ScSpecTypeMap`, `ScSpecTypeTuple`, `ScSpecTypeBytesN`, `ScSpecTypeUdt`, `ScSpecTypeDef`, `ScSpecUdtStructFieldV0`, `ScSpecUdtStructV0`, `ScSpecUdtUnionCaseVoidV0`, `ScSpecUdtUnionCaseTupleV0`, `ScSpecUdtUnionCaseV0Kind`, `ScSpecUdtUnionCaseV0`, `ScSpecUdtUnionV0`, `ScSpecUdtEnumCaseV0`, `ScSpecUdtEnumV0`, `ScSpecUdtErrorEnumCaseV0`, `ScSpecUdtErrorEnumV0`, `ScSpecFunctionInputV0`, `ScSpecFunctionV0`, `ScSpecEntryKind`, `ScSpecEntry`, `ScValType`, `ScErrorType`, `ScErrorCode`, `ScError`, `UInt128Parts`, `Int128Parts`, `UInt256Parts`, `Int256Parts`, `ContractExecutableType`, `ContractExecutable`, `ScAddressType`, `ScAddress`, `ScVec`, `ScMap`, `ScBytes`, `ScString`, `ScSymbol`, `ScNonceKey`, `ScContractInstance`, `ScVal`, `ScMapEntry`, `StoredTransactionSet`, `PersistedScpStateV0`, `PersistedScpStateV1`, `PersistedScpState`, `Thresholds`, `String32`, `String64`, `SequenceNumber`, `DataValue`, `PoolId`, `AssetCode4`, `AssetCode12`, `AssetType`, `AssetCode`, `AlphaNum4`, `AlphaNum12`, `Asset`, `Price`, `Liabilities`, `ThresholdIndexes`, `LedgerEntryType`, `Signer`, `AccountFlags`, `SponsorshipDescriptor`, `AccountEntryExtensionV3`, `AccountEntryExtensionV2`, `AccountEntryExtensionV2Ext`, `AccountEntryExtensionV1`, `AccountEntryExtensionV1Ext`, `AccountEntry`, `AccountEntryExt`, `TrustLineFlags`, `LiquidityPoolType`, `TrustLineAsset`, `TrustLineEntryExtensionV2`, `TrustLineEntryExtensionV2Ext`, `TrustLineEntry`, `TrustLineEntryExt`, `TrustLineEntryV1`, `TrustLineEntryV1Ext`, `OfferEntryFlags`, `OfferEntry`, `OfferEntryExt`, `DataEntry`, `DataEntryExt`, `ClaimPredicateType`, `ClaimPredicate`, `ClaimantType`, `Claimant`, `ClaimantV0`, `ClaimableBalanceIdType`, `ClaimableBalanceId`, `ClaimableBalanceFlags`, `ClaimableBalanceEntryExtensionV1`, `ClaimableBalanceEntryExtensionV1Ext`, `ClaimableBalanceEntry`, `ClaimableBalanceEntryExt`, `LiquidityPoolConstantProductParameters`, `LiquidityPoolEntry`, `LiquidityPoolEntryBody`, `LiquidityPoolEntryConstantProduct`, `ContractEntryBodyType`, `ContractDataFlags`, `ContractDataDurability`, `ContractDataEntry`, `ContractDataEntryBody`, `ContractDataEntryData`, `ContractCodeEntry`, `ContractCodeEntryBody`, `LedgerEntryExtensionV1`, `LedgerEntryExtensionV1Ext`, `LedgerEntry`, `LedgerEntryData`, `LedgerEntryExt`, `LedgerKey`, `LedgerKeyAccount`, `LedgerKeyTrustLine`, `LedgerKeyOffer`, `LedgerKeyData`, `LedgerKeyClaimableBalance`, `LedgerKeyLiquidityPool`, `LedgerKeyContractData`, `LedgerKeyContractCode`, `LedgerKeyConfigSetting`, `EnvelopeType`, `UpgradeType`, `StellarValueType`, `LedgerCloseValueSignature`, `StellarValue`, `StellarValueExt`, `LedgerHeaderFlags`, `LedgerHeaderExtensionV1`, `LedgerHeaderExtensionV1Ext`, `LedgerHeader`, `LedgerHeaderExt`, `LedgerUpgradeType`, `ConfigUpgradeSetKey`, `LedgerUpgrade`, `ConfigUpgradeSet`, `BucketEntryType`, `BucketMetadata`, `BucketMetadataExt`, `BucketEntry`, `TxSetComponentType`, `TxSetComponent`, `TxSetComponentTxsMaybeDiscountedFee`, `TransactionPhase`, `TransactionSet`, `TransactionSetV1`, `GeneralizedTransactionSet`, `TransactionResultPair`, `TransactionResultSet`, `TransactionHistoryEntry`, `TransactionHistoryEntryExt`, `TransactionHistoryResultEntry`, `TransactionHistoryResultEntryExt`, `LedgerHeaderHistoryEntry`, `LedgerHeaderHistoryEntryExt`, `LedgerScpMessages`, `ScpHistoryEntryV0`, `ScpHistoryEntry`, `LedgerEntryChangeType`, `LedgerEntryChange`, `LedgerEntryChanges`, `OperationMeta`, `TransactionMetaV1`, `TransactionMetaV2`, `ContractEventType`, `ContractEvent`, `ContractEventBody`, `ContractEventV0`, `DiagnosticEvent`, `SorobanTransactionMeta`, `TransactionMetaV3`, `InvokeHostFunctionSuccessPreImage`, `TransactionMeta`, `TransactionResultMeta`, `UpgradeEntryMeta`, `LedgerCloseMetaV0`, `LedgerCloseMetaV1`, `LedgerCloseMetaV2`, `LedgerCloseMeta`, `ErrorCode`, `SError`, `SendMore`, `SendMoreExtended`, `AuthCert`, `Hello`, `Auth`, `IpAddrType`, `PeerAddress`, `PeerAddressIp`, `MessageType`, `DontHave`, `SurveyMessageCommandType`, `SurveyMessageResponseType`, `SurveyRequestMessage`, `SignedSurveyRequestMessage`, `EncryptedBody`, `SurveyResponseMessage`, `SignedSurveyResponseMessage`, `PeerStats`, `PeerStatList`, `TopologyResponseBodyV0`, `TopologyResponseBodyV1`, `SurveyResponseBody`, `TxAdvertVector`, `FloodAdvert`, `TxDemandVector`, `FloodDemand`, `StellarMessage`, `AuthenticatedMessage`, `AuthenticatedMessageV0`, `LiquidityPoolParameters`, `MuxedAccount`, `MuxedAccountMed25519`, `DecoratedSignature`, `OperationType`, `CreateAccountOp`, `PaymentOp`, `PathPaymentStrictReceiveOp`, `PathPaymentStrictSendOp`, `ManageSellOfferOp`, `ManageBuyOfferOp`, `CreatePassiveSellOfferOp`, `SetOptionsOp`, `ChangeTrustAsset`, `ChangeTrustOp`, `AllowTrustOp`, `ManageDataOp`, `BumpSequenceOp`, `CreateClaimableBalanceOp`, `ClaimClaimableBalanceOp`, `BeginSponsoringFutureReservesOp`, `RevokeSponsorshipType`, `RevokeSponsorshipOp`, `RevokeSponsorshipOpSigner`, `ClawbackOp`, `ClawbackClaimableBalanceOp`, `SetTrustLineFlagsOp`, `LiquidityPoolDepositOp`, `LiquidityPoolWithdrawOp`, `HostFunctionType`, `ContractIdPreimageType`, `ContractIdPreimage`, `ContractIdPreimageFromAddress`, `CreateContractArgs`, `InvokeContractArgs`, `HostFunction`, `SorobanAuthorizedFunctionType`, `SorobanAuthorizedFunction`, `SorobanAuthorizedInvocation`, `SorobanAddressCredentials`, `SorobanCredentialsType`, `SorobanCredentials`, `SorobanAuthorizationEntry`, `InvokeHostFunctionOp`, `BumpFootprintExpirationOp`, `RestoreFootprintOp`, `Operation`, `OperationBody`, `HashIdPreimage`, `HashIdPreimageOperationId`, `HashIdPreimageRevokeId`, `HashIdPreimageContractId`, `HashIdPreimageSorobanAuthorization`, `MemoType`, `Memo`, `TimeBounds`, `LedgerBounds`, `PreconditionsV2`, `PreconditionType`, `Preconditions`, `LedgerFootprint`, `SorobanResources`, `SorobanTransactionData`, `TransactionV0`, `TransactionV0Ext`, `TransactionV0Envelope`, `Transaction`, `TransactionExt`, `TransactionV1Envelope`, `FeeBumpTransaction`, `FeeBumpTransactionInnerTx`, `FeeBumpTransactionExt`, `FeeBumpTransactionEnvelope`, `TransactionEnvelope`, `TransactionSignaturePayload`, `TransactionSignaturePayloadTaggedTransaction`, `ClaimAtomType`, `ClaimOfferAtomV0`, `ClaimOfferAtom`, `ClaimLiquidityAtom`, `ClaimAtom`, `CreateAccountResultCode`, `CreateAccountResult`, `PaymentResultCode`, `PaymentResult`, `PathPaymentStrictReceiveResultCode`, `SimplePaymentResult`, `PathPaymentStrictReceiveResult`, `PathPaymentStrictReceiveResultSuccess`, `PathPaymentStrictSendResultCode`, `PathPaymentStrictSendResult`, `PathPaymentStrictSendResultSuccess`, `ManageSellOfferResultCode`, `ManageOfferEffect`, `ManageOfferSuccessResult`, `ManageOfferSuccessResultOffer`, `ManageSellOfferResult`, `ManageBuyOfferResultCode`, `ManageBuyOfferResult`, `SetOptionsResultCode`, `SetOptionsResult`, `ChangeTrustResultCode`, `ChangeTrustResult`, `AllowTrustResultCode`, `AllowTrustResult`, `AccountMergeResultCode`, `AccountMergeResult`, `InflationResultCode`, `InflationPayout`, `InflationResult`, `ManageDataResultCode`, `ManageDataResult`, `BumpSequenceResultCode`, `BumpSequenceResult`, `CreateClaimableBalanceResultCode`, `CreateClaimableBalanceResult`, `ClaimClaimableBalanceResultCode`, `ClaimClaimableBalanceResult`, `BeginSponsoringFutureReservesResultCode`, `BeginSponsoringFutureReservesResult`, `EndSponsoringFutureReservesResultCode`, `EndSponsoringFutureReservesResult`, `RevokeSponsorshipResultCode`, `RevokeSponsorshipResult`, `ClawbackResultCode`, `ClawbackResult`, `ClawbackClaimableBalanceResultCode`, `ClawbackClaimableBalanceResult`, `SetTrustLineFlagsResultCode`, `SetTrustLineFlagsResult`, `LiquidityPoolDepositResultCode`, `LiquidityPoolDepositResult`, `LiquidityPoolWithdrawResultCode`, `LiquidityPoolWithdrawResult`, `InvokeHostFunctionResultCode`, `InvokeHostFunctionResult`, `BumpFootprintExpirationResultCode`, `BumpFootprintExpirationResult`, `RestoreFootprintResultCode`, `RestoreFootprintResult`, `OperationResultCode`, `OperationResult`, `OperationResultTr`, `TransactionResultCode`, `InnerTransactionResult`, `InnerTransactionResultResult`, `InnerTransactionResultExt`, `InnerTransactionResultPair`, `TransactionResult`, `TransactionResultResult`, `TransactionResultExt`, `Hash`, `Uint256`, `Uint32`, `Int32`, `Uint64`, `Int64`, `TimePoint`, `Duration`, `ExtensionPoint`, `CryptoKeyType`, `PublicKeyType`, `SignerKeyType`, `PublicKey`, `SignerKey`, `SignerKeyEd25519SignedPayload`, `Signature`, `SignatureHint`, `NodeId`, `AccountId`, `Curve25519Secret`, `Curve25519Public`, `HmacSha256Key`, `HmacSha256Mac` + Possible values: `Value`, `ScpBallot`, `ScpStatementType`, `ScpNomination`, `ScpStatement`, `ScpStatementPledges`, `ScpStatementPrepare`, `ScpStatementConfirm`, `ScpStatementExternalize`, `ScpEnvelope`, `ScpQuorumSet`, `ConfigSettingContractExecutionLanesV0`, `ConfigSettingContractComputeV0`, `ConfigSettingContractLedgerCostV0`, `ConfigSettingContractHistoricalDataV0`, `ConfigSettingContractEventsV0`, `ConfigSettingContractBandwidthV0`, `ContractCostType`, `ContractCostParamEntry`, `StateExpirationSettings`, `EvictionIterator`, `ContractCostParams`, `ConfigSettingId`, `ConfigSettingEntry`, `ScEnvMetaKind`, `ScEnvMetaEntry`, `ScMetaV0`, `ScMetaKind`, `ScMetaEntry`, `ScSpecType`, `ScSpecTypeOption`, `ScSpecTypeResult`, `ScSpecTypeVec`, `ScSpecTypeMap`, `ScSpecTypeTuple`, `ScSpecTypeBytesN`, `ScSpecTypeUdt`, `ScSpecTypeDef`, `ScSpecUdtStructFieldV0`, `ScSpecUdtStructV0`, `ScSpecUdtUnionCaseVoidV0`, `ScSpecUdtUnionCaseTupleV0`, `ScSpecUdtUnionCaseV0Kind`, `ScSpecUdtUnionCaseV0`, `ScSpecUdtUnionV0`, `ScSpecUdtEnumCaseV0`, `ScSpecUdtEnumV0`, `ScSpecUdtErrorEnumCaseV0`, `ScSpecUdtErrorEnumV0`, `ScSpecFunctionInputV0`, `ScSpecFunctionV0`, `ScSpecEntryKind`, `ScSpecEntry`, `ScValType`, `ScErrorType`, `ScErrorCode`, `ScError`, `UInt128Parts`, `Int128Parts`, `UInt256Parts`, `Int256Parts`, `ContractExecutableType`, `ContractExecutable`, `ScAddressType`, `ScAddress`, `ScVec`, `ScMap`, `ScBytes`, `ScString`, `ScSymbol`, `ScNonceKey`, `ScContractInstance`, `ScVal`, `ScMapEntry`, `StoredTransactionSet`, `PersistedScpStateV0`, `PersistedScpStateV1`, `PersistedScpState`, `Thresholds`, `String32`, `String64`, `SequenceNumber`, `DataValue`, `PoolId`, `AssetCode4`, `AssetCode12`, `AssetType`, `AssetCode`, `AlphaNum4`, `AlphaNum12`, `Asset`, `Price`, `Liabilities`, `ThresholdIndexes`, `LedgerEntryType`, `Signer`, `AccountFlags`, `SponsorshipDescriptor`, `AccountEntryExtensionV3`, `AccountEntryExtensionV2`, `AccountEntryExtensionV2Ext`, `AccountEntryExtensionV1`, `AccountEntryExtensionV1Ext`, `AccountEntry`, `AccountEntryExt`, `TrustLineFlags`, `LiquidityPoolType`, `TrustLineAsset`, `TrustLineEntryExtensionV2`, `TrustLineEntryExtensionV2Ext`, `TrustLineEntry`, `TrustLineEntryExt`, `TrustLineEntryV1`, `TrustLineEntryV1Ext`, `OfferEntryFlags`, `OfferEntry`, `OfferEntryExt`, `DataEntry`, `DataEntryExt`, `ClaimPredicateType`, `ClaimPredicate`, `ClaimantType`, `Claimant`, `ClaimantV0`, `ClaimableBalanceIdType`, `ClaimableBalanceId`, `ClaimableBalanceFlags`, `ClaimableBalanceEntryExtensionV1`, `ClaimableBalanceEntryExtensionV1Ext`, `ClaimableBalanceEntry`, `ClaimableBalanceEntryExt`, `LiquidityPoolConstantProductParameters`, `LiquidityPoolEntry`, `LiquidityPoolEntryBody`, `LiquidityPoolEntryConstantProduct`, `ContractDataDurability`, `ContractDataEntry`, `ContractCodeEntry`, `ExpirationEntry`, `LedgerEntryExtensionV1`, `LedgerEntryExtensionV1Ext`, `LedgerEntry`, `LedgerEntryData`, `LedgerEntryExt`, `LedgerKey`, `LedgerKeyAccount`, `LedgerKeyTrustLine`, `LedgerKeyOffer`, `LedgerKeyData`, `LedgerKeyClaimableBalance`, `LedgerKeyLiquidityPool`, `LedgerKeyContractData`, `LedgerKeyContractCode`, `LedgerKeyConfigSetting`, `LedgerKeyExpiration`, `EnvelopeType`, `UpgradeType`, `StellarValueType`, `LedgerCloseValueSignature`, `StellarValue`, `StellarValueExt`, `LedgerHeaderFlags`, `LedgerHeaderExtensionV1`, `LedgerHeaderExtensionV1Ext`, `LedgerHeader`, `LedgerHeaderExt`, `LedgerUpgradeType`, `ConfigUpgradeSetKey`, `LedgerUpgrade`, `ConfigUpgradeSet`, `BucketEntryType`, `BucketMetadata`, `BucketMetadataExt`, `BucketEntry`, `TxSetComponentType`, `TxSetComponent`, `TxSetComponentTxsMaybeDiscountedFee`, `TransactionPhase`, `TransactionSet`, `TransactionSetV1`, `GeneralizedTransactionSet`, `TransactionResultPair`, `TransactionResultSet`, `TransactionHistoryEntry`, `TransactionHistoryEntryExt`, `TransactionHistoryResultEntry`, `TransactionHistoryResultEntryExt`, `LedgerHeaderHistoryEntry`, `LedgerHeaderHistoryEntryExt`, `LedgerScpMessages`, `ScpHistoryEntryV0`, `ScpHistoryEntry`, `LedgerEntryChangeType`, `LedgerEntryChange`, `LedgerEntryChanges`, `OperationMeta`, `TransactionMetaV1`, `TransactionMetaV2`, `ContractEventType`, `ContractEvent`, `ContractEventBody`, `ContractEventV0`, `DiagnosticEvent`, `SorobanTransactionMeta`, `TransactionMetaV3`, `InvokeHostFunctionSuccessPreImage`, `TransactionMeta`, `TransactionResultMeta`, `UpgradeEntryMeta`, `LedgerCloseMetaV0`, `LedgerCloseMetaV1`, `LedgerCloseMetaV2`, `LedgerCloseMeta`, `ErrorCode`, `SError`, `SendMore`, `SendMoreExtended`, `AuthCert`, `Hello`, `Auth`, `IpAddrType`, `PeerAddress`, `PeerAddressIp`, `MessageType`, `DontHave`, `SurveyMessageCommandType`, `SurveyMessageResponseType`, `SurveyRequestMessage`, `SignedSurveyRequestMessage`, `EncryptedBody`, `SurveyResponseMessage`, `SignedSurveyResponseMessage`, `PeerStats`, `PeerStatList`, `TopologyResponseBodyV0`, `TopologyResponseBodyV1`, `SurveyResponseBody`, `TxAdvertVector`, `FloodAdvert`, `TxDemandVector`, `FloodDemand`, `StellarMessage`, `AuthenticatedMessage`, `AuthenticatedMessageV0`, `LiquidityPoolParameters`, `MuxedAccount`, `MuxedAccountMed25519`, `DecoratedSignature`, `OperationType`, `CreateAccountOp`, `PaymentOp`, `PathPaymentStrictReceiveOp`, `PathPaymentStrictSendOp`, `ManageSellOfferOp`, `ManageBuyOfferOp`, `CreatePassiveSellOfferOp`, `SetOptionsOp`, `ChangeTrustAsset`, `ChangeTrustOp`, `AllowTrustOp`, `ManageDataOp`, `BumpSequenceOp`, `CreateClaimableBalanceOp`, `ClaimClaimableBalanceOp`, `BeginSponsoringFutureReservesOp`, `RevokeSponsorshipType`, `RevokeSponsorshipOp`, `RevokeSponsorshipOpSigner`, `ClawbackOp`, `ClawbackClaimableBalanceOp`, `SetTrustLineFlagsOp`, `LiquidityPoolDepositOp`, `LiquidityPoolWithdrawOp`, `HostFunctionType`, `ContractIdPreimageType`, `ContractIdPreimage`, `ContractIdPreimageFromAddress`, `CreateContractArgs`, `InvokeContractArgs`, `HostFunction`, `SorobanAuthorizedFunctionType`, `SorobanAuthorizedFunction`, `SorobanAuthorizedInvocation`, `SorobanAddressCredentials`, `SorobanCredentialsType`, `SorobanCredentials`, `SorobanAuthorizationEntry`, `InvokeHostFunctionOp`, `BumpFootprintExpirationOp`, `RestoreFootprintOp`, `Operation`, `OperationBody`, `HashIdPreimage`, `HashIdPreimageOperationId`, `HashIdPreimageRevokeId`, `HashIdPreimageContractId`, `HashIdPreimageSorobanAuthorization`, `MemoType`, `Memo`, `TimeBounds`, `LedgerBounds`, `PreconditionsV2`, `PreconditionType`, `Preconditions`, `LedgerFootprint`, `SorobanResources`, `SorobanTransactionData`, `TransactionV0`, `TransactionV0Ext`, `TransactionV0Envelope`, `Transaction`, `TransactionExt`, `TransactionV1Envelope`, `FeeBumpTransaction`, `FeeBumpTransactionInnerTx`, `FeeBumpTransactionExt`, `FeeBumpTransactionEnvelope`, `TransactionEnvelope`, `TransactionSignaturePayload`, `TransactionSignaturePayloadTaggedTransaction`, `ClaimAtomType`, `ClaimOfferAtomV0`, `ClaimOfferAtom`, `ClaimLiquidityAtom`, `ClaimAtom`, `CreateAccountResultCode`, `CreateAccountResult`, `PaymentResultCode`, `PaymentResult`, `PathPaymentStrictReceiveResultCode`, `SimplePaymentResult`, `PathPaymentStrictReceiveResult`, `PathPaymentStrictReceiveResultSuccess`, `PathPaymentStrictSendResultCode`, `PathPaymentStrictSendResult`, `PathPaymentStrictSendResultSuccess`, `ManageSellOfferResultCode`, `ManageOfferEffect`, `ManageOfferSuccessResult`, `ManageOfferSuccessResultOffer`, `ManageSellOfferResult`, `ManageBuyOfferResultCode`, `ManageBuyOfferResult`, `SetOptionsResultCode`, `SetOptionsResult`, `ChangeTrustResultCode`, `ChangeTrustResult`, `AllowTrustResultCode`, `AllowTrustResult`, `AccountMergeResultCode`, `AccountMergeResult`, `InflationResultCode`, `InflationPayout`, `InflationResult`, `ManageDataResultCode`, `ManageDataResult`, `BumpSequenceResultCode`, `BumpSequenceResult`, `CreateClaimableBalanceResultCode`, `CreateClaimableBalanceResult`, `ClaimClaimableBalanceResultCode`, `ClaimClaimableBalanceResult`, `BeginSponsoringFutureReservesResultCode`, `BeginSponsoringFutureReservesResult`, `EndSponsoringFutureReservesResultCode`, `EndSponsoringFutureReservesResult`, `RevokeSponsorshipResultCode`, `RevokeSponsorshipResult`, `ClawbackResultCode`, `ClawbackResult`, `ClawbackClaimableBalanceResultCode`, `ClawbackClaimableBalanceResult`, `SetTrustLineFlagsResultCode`, `SetTrustLineFlagsResult`, `LiquidityPoolDepositResultCode`, `LiquidityPoolDepositResult`, `LiquidityPoolWithdrawResultCode`, `LiquidityPoolWithdrawResult`, `InvokeHostFunctionResultCode`, `InvokeHostFunctionResult`, `BumpFootprintExpirationResultCode`, `BumpFootprintExpirationResult`, `RestoreFootprintResultCode`, `RestoreFootprintResult`, `OperationResultCode`, `OperationResult`, `OperationResultTr`, `TransactionResultCode`, `InnerTransactionResult`, `InnerTransactionResultResult`, `InnerTransactionResultExt`, `InnerTransactionResultPair`, `TransactionResult`, `TransactionResultResult`, `TransactionResultExt`, `Hash`, `Uint256`, `Uint32`, `Int32`, `Uint64`, `Int64`, `TimePoint`, `Duration`, `ExtensionPoint`, `CryptoKeyType`, `PublicKeyType`, `SignerKeyType`, `PublicKey`, `SignerKey`, `SignerKeyEd25519SignedPayload`, `Signature`, `SignatureHint`, `NodeId`, `AccountId`, `Curve25519Secret`, `Curve25519Public`, `HmacSha256Key`, `HmacSha256Mac` * `--xdr ` — XDR (base64 encoded) to decode * `--output ` — Type of output diff --git a/go.mod b/go.mod index 4012ee58f..c6773f33e 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - github.com/stellar/go v0.0.0-20230829095205-a541e69acb5f + github.com/stellar/go v0.0.0-20230901002747-400590c5cce4 github.com/stretchr/testify v1.8.4 golang.org/x/mod v0.12.0 ) diff --git a/go.sum b/go.sum index 34e369b28..0af0d6452 100644 --- a/go.sum +++ b/go.sum @@ -186,10 +186,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/stellar/go v0.0.0-20230825090709-76e07a4dbd21 h1:OqDQXwl/IvYX4WNJJSQXxbArjjeCcoHgfnaZPndbc58= -github.com/stellar/go v0.0.0-20230825090709-76e07a4dbd21/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= -github.com/stellar/go v0.0.0-20230829095205-a541e69acb5f h1:bNIzaQjSYAOAkbAK9g2zY4dykWEltK5LCuEe4uEoygc= -github.com/stellar/go v0.0.0-20230829095205-a541e69acb5f/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= +github.com/stellar/go v0.0.0-20230901002747-400590c5cce4 h1:SUkKaeW+G+KQ/LJwkN+li/ws/aSpJVsyZW1uhJHPY4k= +github.com/stellar/go v0.0.0-20230901002747-400590c5cce4/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee h1:fbVs0xmXpBvVS4GBeiRmAE3Le70ofAqFMch1GTiq/e8= github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=