Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a more generic getLedgerEntry rpc method #274

Merged
merged 13 commits into from
Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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