Skip to content

Commit

Permalink
Add a more generic getLedgerEntry rpc method (#274)
Browse files Browse the repository at this point in the history
* Add rpc getLedgerEntry method

* fix getContractID

* Fixing tests

* updating rust xdr and sdk

* updating to new hostfunction format

* updating soroban-cli to use getLedgerEntry

* rustfmt

* WIP -- updating for new xdr

* WIP -- updating for new xdr

* Updating xdr for soroban-rpc (#279)

* Updating xdr for soroban-rpc

* Trying the new core version

* Fix nil pointer

* Need to compare contract hash not contract code now

* getLedgerEntry test checks code not hash

* Fixing tests

* update go mod/sum to reflect the latest soroban-xdr-next branch in go monorepo

* Update sdk and env to latest crates

* Fix up conversions and clippy

* Need to use the sandbox passphrase in the sandbox

* update test mocks for new contract ids

* Updating contract ids for e2e

Co-authored-by: tsachiherman <[email protected]>

Co-authored-by: tsachiherman <[email protected]>
  • Loading branch information
Paul Bellamy and tsachiherman authored Dec 2, 2022
1 parent 29e1357 commit 255015e
Show file tree
Hide file tree
Showing 23 changed files with 1,142 additions and 487 deletions.
577 changes: 367 additions & 210 deletions Cargo.lock

Large diffs are not rendered by default.

19 changes: 9 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ serde = "1.0.82"
serde_derive = "1.0.82"
serde_json = "1.0.82"
hex = "0.4.3"
num-bigint = "0.4"
tokio = { version = "1", features = ["full"] }
warp = "0.3"
clap_complete = "3.2.3"
Expand All @@ -58,29 +57,29 @@ members = [
default-members = ["cmd/soroban-cli"]

[workspace.dependencies.soroban-env-host]
version = "0.0.9"
version = "0.0.10"
git = "https://github.com/stellar/rs-soroban-env"
rev = "99de1bf0"
rev = "c148051"

[workspace.dependencies.soroban-spec]
version = "0.2.1"
version = "0.3.0"
git = "https://github.com/stellar/rs-soroban-sdk"
rev = "63bef69c"
rev = "501379d"

[workspace.dependencies.soroban-token-spec]
version = "0.2.1"
version = "0.3.0"
git = "https://github.com/stellar/rs-soroban-sdk"
rev = "63bef69c"
rev = "501379d"

[workspace.dependencies.soroban-sdk]
version = "0.2.1"
version = "0.3.0"
git = "https://github.com/stellar/rs-soroban-sdk"
rev = "63bef69c"
rev = "501379d"

[workspace.dependencies.stellar-strkey]
version = "0.0.6"
git = "https://github.com/stellar/rs-stellar-strkey"
rev = "d1b68fd1"
rev = "5e582a8"

# [patch."https://github.com/stellar/rs-soroban-env"]
# soroban-env-host = { path = "../rs-soroban-env/soroban-env-host/" }
Expand Down
109 changes: 87 additions & 22 deletions cmd/soroban-cli/src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ use rand::Rng;
use sha2::{Digest, Sha256};
use soroban_env_host::xdr::HashIdPreimageSourceAccountContractId;
use soroban_env_host::xdr::{
AccountId, Error as XdrError, Hash, HashIdPreimage, HostFunction, InvokeHostFunctionOp,
LedgerFootprint, LedgerKey::ContractData, LedgerKeyContractData, Memo, MuxedAccount, Operation,
OperationBody, Preconditions, PublicKey, ScObject, ScStatic::LedgerKeyContractCode, ScVal,
SequenceNumber, Transaction, TransactionEnvelope, TransactionExt, Uint256, VecM, WriteXdr,
AccountId, ContractId, CreateContractArgs, Error as XdrError, Hash, HashIdPreimage,
HostFunction, InstallContractCodeArgs, InvokeHostFunctionOp, LedgerFootprint,
LedgerKey::ContractCode, LedgerKey::ContractData, LedgerKeyContractCode, LedgerKeyContractData,
Memo, MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScContractCode,
ScStatic, ScVal, SequenceNumber, Transaction, TransactionEnvelope, TransactionExt, Uint256,
VecM, WriteXdr,
};
use soroban_env_host::HostError;

Expand Down Expand Up @@ -178,31 +180,80 @@ impl Cmd {
// TODO: create a cmdline parameter for the fee instead of simply using the minimum fee
let fee: u32 = 100;
let sequence = account_details.sequence.parse::<i64>()?;
let (tx, contract_id) = build_create_contract_tx(

let (tx, hash) = build_install_contract_code_tx(
contract,
sequence + 1,
fee,
self.network_passphrase.as_ref().unwrap(),
salt,
&key,
)?;
client.send_transaction(&tx).await?;

let (tx, contract_id) = build_create_contract_tx(
hash,
sequence + 2,
fee,
self.network_passphrase.as_ref().unwrap(),
salt,
&key,
)?;
client.send_transaction(&tx).await?;

Ok(hex::encode(contract_id.0))
}
}

fn build_create_contract_tx(
fn build_install_contract_code_tx(
contract: Vec<u8>,
sequence: i64,
fee: u32,
network_passphrase: &str,
key: &ed25519_dalek::Keypair,
) -> Result<(TransactionEnvelope, Hash), Error> {
let hash = utils::contract_hash(&contract)?;

let op = Operation {
source_account: None,
body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp {
function: HostFunction::InstallContractCode(InstallContractCodeArgs {
code: contract.try_into()?,
}),
footprint: LedgerFootprint {
read_only: VecM::default(),
read_write: vec![ContractCode(LedgerKeyContractCode { hash: hash.clone() })]
.try_into()?,
},
}),
};

let tx = Transaction {
source_account: MuxedAccount::Ed25519(Uint256(key.public.to_bytes())),
fee,
seq_num: SequenceNumber(sequence),
cond: Preconditions::None,
memo: Memo::None,
operations: vec![op].try_into()?,
ext: TransactionExt::V0,
};

let envelope = utils::sign_transaction(key, &tx, network_passphrase)?;

Ok((envelope, hash))
}

fn build_create_contract_tx(
hash: Hash,
sequence: i64,
fee: u32,
network_passphrase: &str,
salt: [u8; 32],
key: &ed25519_dalek::Keypair,
) -> Result<(TransactionEnvelope, Hash), Error> {
let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into());
let preimage =
HashIdPreimage::ContractIdFromSourceAccount(HashIdPreimageSourceAccountContractId {
network_id,
source_account: AccountId(PublicKey::PublicKeyTypeEd25519(
key.public.to_bytes().into(),
)),
Expand All @@ -211,24 +262,20 @@ fn build_create_contract_tx(
let preimage_xdr = preimage.to_xdr()?;
let contract_id = Sha256::digest(preimage_xdr);

let contract_parameter = ScVal::Object(Some(ScObject::Bytes(contract.try_into()?)));
let salt_parameter = ScVal::Object(Some(ScObject::Bytes(salt.try_into()?)));

let lk = ContractData(LedgerKeyContractData {
contract_id: Hash(contract_id.into()),
key: ScVal::Static(LedgerKeyContractCode),
});

let parameters: VecM<ScVal, 256_000> = vec![contract_parameter, salt_parameter].try_into()?;

let op = Operation {
source_account: None,
body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp {
function: HostFunction::CreateContractWithSourceAccount,
parameters: parameters.into(),
function: HostFunction::CreateContract(CreateContractArgs {
contract_id: ContractId::SourceAccount(Uint256(salt)),
source: ScContractCode::WasmRef(hash.clone()),
}),
footprint: LedgerFootprint {
read_only: VecM::default(),
read_write: vec![lk].try_into()?,
read_only: vec![ContractCode(LedgerKeyContractCode { hash })].try_into()?,
read_write: vec![ContractData(LedgerKeyContractData {
contract_id: Hash(contract_id.into()),
key: ScVal::Static(ScStatic::LedgerKeyContractCode),
})]
.try_into()?,
},
}),
};
Expand All @@ -251,10 +298,28 @@ fn build_create_contract_tx(
mod tests {
use super::*;

#[test]
fn test_build_install_contract_code() {
let result = build_install_contract_code_tx(
b"foo".to_vec(),
300,
1,
"Public Global Stellar Network ; September 2015",
&utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP")
.unwrap(),
);

assert!(result.is_ok());
}

#[test]
fn test_build_create_contract() {
let hash = hex::decode("0000000000000000000000000000000000000000000000000000000000000000")
.unwrap()
.try_into()
.unwrap();
let result = build_create_contract_tx(
b"foo".to_vec(),
Hash(hash),
300,
1,
"Public Global Stellar Network ; September 2015",
Expand Down
70 changes: 47 additions & 23 deletions cmd/soroban-cli/src/invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use std::{fmt::Debug, fs, io, rc::Rc};
use clap::Parser;
use hex::FromHexError;
use soroban_env_host::xdr::{
InvokeHostFunctionOp, LedgerFootprint, LedgerKey, LedgerKeyAccount, Memo, MuxedAccount,
Operation, OperationBody, Preconditions, ScStatic, ScVec, SequenceNumber, Transaction,
TransactionEnvelope, TransactionExt, VecM,
self, ContractCodeEntry, ContractDataEntry, InvokeHostFunctionOp, LedgerEntryData,
LedgerFootprint, LedgerKey, LedgerKeyAccount, LedgerKeyContractCode, LedgerKeyContractData,
Memo, MuxedAccount, Operation, OperationBody, Preconditions, ScContractCode, ScStatic, ScVec,
SequenceNumber, Transaction, TransactionEnvelope, TransactionExt, VecM,
};
use soroban_env_host::{
budget::{Budget, CostType},
Expand All @@ -22,9 +23,7 @@ use soroban_spec::read::FromWasmError;
use stellar_strkey::StrkeyPublicKeyEd25519;

use crate::rpc::Client;
use crate::utils::{
contract_code_to_spec_entries, create_ledger_footprint, default_account_ledger_entry,
};
use crate::utils::{create_ledger_footprint, default_account_ledger_entry};
use crate::{
rpc, snapshot,
strval::{self, StrValError},
Expand Down Expand Up @@ -160,7 +159,7 @@ pub enum Error {
#[error(transparent)]
Rpc(#[from] rpc::Error),
#[error("unexpected contract code data type: {0:?}")]
UnexpectedContractCodeDataType(ScVal),
UnexpectedContractCodeDataType(LedgerEntryData),
#[error("missing transaction result")]
MissingTransactionResult,
}
Expand Down Expand Up @@ -295,19 +294,7 @@ impl Cmd {
})?;
soroban_spec::read::from_wasm(&wasm).map_err(Error::CannotParseContractSpec)?
} else {
// Get the contract from the network
let contract_data = client
.get_contract_data(
&hex::encode(contract_id),
ScVal::Static(ScStatic::LedgerKeyContractCode),
)
.await?;
match ScVal::from_xdr_base64(contract_data.xdr)? {
ScVal::Object(Some(ScObject::ContractCode(c))) => {
contract_code_to_spec_entries(c).map_err(Error::CannotParseContractSpec)?
}
scval => return Err(Error::UnexpectedContractCodeDataType(scval)),
}
get_remote_contract_spec_entries(&client, &contract_id).await?
};

// Get the ledger footprint
Expand Down Expand Up @@ -406,7 +393,7 @@ impl Cmd {
let host_function_params =
self.build_host_function_parameters(contract_id, &spec_entries, matches)?;

let res = h.invoke_function(HostFunction::InvokeContract, host_function_params)?;
let res = h.invoke_function(HostFunction::InvokeContract(host_function_params))?;
let res_str = strval::to_string(&res).map_err(|e| Error::CannotPrintResult {
result: res,
error: e,
Expand Down Expand Up @@ -471,8 +458,7 @@ fn build_invoke_contract_tx(
let op = Operation {
source_account: None,
body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp {
function: HostFunction::InvokeContract,
parameters,
function: HostFunction::InvokeContract(parameters),
footprint: final_footprint,
}),
};
Expand All @@ -488,3 +474,41 @@ fn build_invoke_contract_tx(

Ok(utils::sign_transaction(key, &tx, network_passphrase)?)
}

async fn get_remote_contract_spec_entries(
client: &Client,
contract_id: &[u8; 32],
) -> Result<Vec<ScSpecEntry>, Error> {
// Get the contract from the network
let contract_ref = client
.get_ledger_entry(LedgerKey::ContractData(LedgerKeyContractData {
contract_id: xdr::Hash(*contract_id),
key: ScVal::Static(ScStatic::LedgerKeyContractCode),
}))
.await?;

Ok(match LedgerEntryData::from_xdr_base64(contract_ref.xdr)? {
LedgerEntryData::ContractData(ContractDataEntry {
val: ScVal::Object(Some(ScObject::ContractCode(ScContractCode::WasmRef(hash)))),
..
}) => {
let contract_data = client
.get_ledger_entry(LedgerKey::ContractCode(LedgerKeyContractCode { hash }))
.await?;

match LedgerEntryData::from_xdr_base64(contract_data.xdr)? {
LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }) => {
soroban_spec::read::from_wasm(&code).map_err(Error::CannotParseContractSpec)?
}
scval => return Err(Error::UnexpectedContractCodeDataType(scval)),
}
}
LedgerEntryData::ContractData(ContractDataEntry {
val: ScVal::Object(Some(ScObject::ContractCode(ScContractCode::Token))),
..
}) => soroban_spec::read::parse_raw(&soroban_token_spec::spec_xdr())
.map_err(FromWasmError::Parse)
.map_err(Error::CannotParseContractSpec)?,
scval => return Err(Error::UnexpectedContractCodeDataType(scval)),
})
}
17 changes: 10 additions & 7 deletions cmd/soroban-cli/src/rpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use jsonrpsee_core::{client::ClientT, rpc_params};
use jsonrpsee_http_client::{HeaderMap, HttpClient, HttpClientBuilder};
use soroban_env_host::xdr::{Error as XdrError, ScVal, TransactionEnvelope, WriteXdr};
use soroban_env_host::xdr::{Error as XdrError, LedgerKey, TransactionEnvelope, WriteXdr};
use std::time::{Duration, Instant};
use tokio::time::sleep;

Expand Down Expand Up @@ -59,6 +59,13 @@ pub struct GetContractDataResponse {
// TODO: add lastModifiedLedgerSeq and latestLedger
}

// TODO: this should also be used by serve
#[derive(serde::Deserialize, serde::Serialize, Debug)]
pub struct GetLedgerEntryResponse {
pub xdr: String,
// TODO: add lastModifiedLedgerSeq and latestLedger
}

// TODO: this should also be used by serve
#[derive(serde::Deserialize, serde::Serialize, Debug)]
pub struct Cost {
Expand Down Expand Up @@ -174,15 +181,11 @@ impl Client {
.await?)
}

pub async fn get_contract_data(
&self,
contract_id: &str,
key: ScVal,
) -> Result<GetContractDataResponse, Error> {
pub async fn get_ledger_entry(&self, key: LedgerKey) -> Result<GetLedgerEntryResponse, Error> {
let base64_key = key.to_xdr_base64()?;
Ok(self
.client()?
.request("getContractData", rpc_params![contract_id, base64_key])
.request("getLedgerEntry", rpc_params![base64_key])
.await?)
}
}
Loading

0 comments on commit 255015e

Please sign in to comment.