Skip to content
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
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 20 additions & 1 deletion starstream-cli/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use tokio::fs;
use tracing::{debug, info, instrument};
use wasmtime::component::{Component, Resource, ResourceTable, Val};
use wasmtime::error::Context as _;
use wasmtime::{AsContextMut as _, Store, StoreContextMut, ensure};
use wasmtime::{AsContextMut as _, Store, StoreContextMut, bail, ensure};

/// Run a coordination script exported by a Wasm component
#[derive(Args, Debug)]
Expand Down Expand Up @@ -228,6 +228,25 @@ async fn exec(
.call_coordination_script(&mut store, &script, &params, &mut results)
.await?;
debug!(outputs = store.data().outputs.len(), "script returned");
'outer: for result in &mut results {
if let &mut Val::Resource(utxo) = result {
let utxo = utxo
.try_into_resource(&mut store)
.context("result resource is not a UTXO")?;
let utxo: &Utxo<Arc<Mutex<UtxoCtx>>> = store
.data()
.table
.get(&utxo)
.context("result UTXO not found")?;
for (i, out) in zip(0.., &store.data().outputs) {
if utxo.resource() == out.resource() {
*result = Val::U32(i);
continue 'outer;
}
}
bail!("failed to identify result resource");
}
}
let results = Val::Tuple(results)
.to_wave()
.context("failed to encode results")?;
Expand Down
3 changes: 2 additions & 1 deletion starstream-ledger-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ hyper-util = { workspace = true, features = [
rand_core = { workspace = true, features = ["getrandom", "std"] }
sha2 = { workspace = true }
starstream-ledger = { workspace = true, features = ["client"] }
starstream-runtime-next = { workspace = true }
toml = { workspace = true, features = ["display", "serde"] }
tokio = { workspace = true, features = [
"fs",
Expand Down Expand Up @@ -67,8 +68,8 @@ zeroize = { workspace = true }
minicbor = { workspace = true, features = ["std"] }
starstream-compiler = { workspace = true }
starstream-ledger = { workspace = true, features = ["server"] }
starstream-runtime-next = { workspace = true }
starstream-to-wasm = { workspace = true }
starstream-types = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true, features = ["fs", "net", "process"] }
toml = { workspace = true, features = ["parse"] }
Expand Down
83 changes: 68 additions & 15 deletions starstream-ledger-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::pin::pin;

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use anyhow::{Context as _, bail, ensure};
use bytes::{Bytes, BytesMut};
Expand All @@ -16,16 +17,19 @@ use hyper_util::rt::TokioExecutor;
use rand_core::OsRng;
use sha2::{Digest as _, Sha256};
use starstream_ledger::client::http::ClientBuilder;
use starstream_ledger::client::runtime::UtxoCtx;
use starstream_ledger::client::runtime::{
Client, call_coordination_script, compile_component, new_contract,
Client, Ctx, call_coordination_script, compile_component, new_contract,
};
use starstream_ledger::client::{decode_transaction, encode_transaction};
use starstream_ledger::{TransactionInput, TransactionOutput, encode_digest};
use starstream_runtime_next::Utxo;
use tokio::fs;
use tokio::io::{AsyncRead, AsyncWriteExt as _, stdout};
use tokio_util::codec::Encoder as _;
use tracing::info;
use wasm_wave::wasm::WasmFunc as _;
use wasmtime::Store;
use wasmtime::component::{Val, types};
use zeroize::Zeroizing;

Expand Down Expand Up @@ -69,6 +73,9 @@ enum Command {
path: PathBuf,
},

/// Print the genesis outputs.
Genesis,

/// Manage signing keys.
#[command(subcommand)]
Key(KeyCommand),
Expand Down Expand Up @@ -193,6 +200,12 @@ enum KeyCommand {

#[derive(Debug, Subcommand)]
enum TransactionCommand {
/// Get a transaction from the ledger.
Get {
/// Digest of the transaction, either as multibase multihash or `sha256:HEX`.
#[arg(value_parser = parse_digest)]
digest: [u8; 32],
},
/// Print a transaction written by `contract script call --output-transaction`.
Show {
/// Path to the encoded transaction.
Expand All @@ -210,7 +223,7 @@ enum UtxoCommand {
transaction: Option<[u8; 32]>,

/// Index of the UTXO in the transaction outputs.
index: usize,
index: u32,

/// Method to call.
method: Box<str>,
Expand Down Expand Up @@ -359,6 +372,16 @@ async fn main() -> anyhow::Result<()> {
.await
.context("failed to write digest to stdout")
}
Command::Genesis => {
let outputs = client.get_genesis().await?;
let outputs = toml::Value::try_from(outputs).context("failed to encode TOML")?;
let genesis = toml::Table::from_iter([("outputs".to_string(), outputs)]);
let genesis = toml::to_string_pretty(&genesis).context("failed to encode TOML")?;
stdout()
.write_all(genesis.as_bytes())
.await
.context("failed to write genesis to stdout")
}
Command::Contract(ContractCommand::Publish {
signing: SigningArgs { key, nonce },
wasm,
Expand Down Expand Up @@ -432,7 +455,10 @@ async fn main() -> anyhow::Result<()> {
ensure!(args.next().is_none(), "trailing arguments");
let mut results = vec![Val::Bool(false); ty.results().len()];

let mut utxos = Vec::default();
let mut store = Store::new(client.engine(), Ctx::default());
let tx = call_coordination_script(
&mut store,
&imports,
client.wizer(),
&contract,
Expand All @@ -441,8 +467,29 @@ async fn main() -> anyhow::Result<()> {
&mut contracts,
params,
&mut results,
&mut utxos,
)
.await?;
'outer: for result in &mut results {
if let &mut Val::Resource(utxo) = result {
let utxo = utxo
.try_into_resource(&mut store)
.map_err(anyhow::Error::from)
.context("result resource is not a UTXO")?;
let utxo: &Utxo<Arc<std::sync::Mutex<UtxoCtx>>> = store
.data()
.table
.get(&utxo)
.context("result UTXO not found")?;
for (i, out) in zip(0.., &utxos) {
if utxo.resource() == out.resource() {
*result = Val::U32(i);
continue 'outer;
}
}
bail!("failed to identify result resource");
}
}
let mut results = wasm_wave::to_string(&Val::Tuple(results))
.context("failed to encode result tuple")?;
if let Some(path) = output_transaction {
Expand Down Expand Up @@ -471,6 +518,14 @@ async fn main() -> anyhow::Result<()> {
.await
.context("failed to write signing key to stdout")
}
Command::Transaction(TransactionCommand::Get { digest }) => {
let tx = client.get_transaction(digest).await?;
let tx = toml::to_string_pretty(&tx).context("failed to encode TOML")?;
stdout()
.write_all(tx.as_bytes())
.await
.context("failed to write transaction to stdout")
}
Command::Transaction(TransactionCommand::Show { path }) => {
let buf = fs::read(&path)
.await
Expand All @@ -488,17 +543,18 @@ async fn main() -> anyhow::Result<()> {
method,
args,
}) => {
let TransactionOutput {
instance,
methods,
storage,
wasm,
..
} = if let Some(transaction) = transaction {
client.get_transaction_utxo(transaction, index).await?
let transaction = if let Some(transaction) = transaction {
encode_digest(&transaction).into()
} else {
client.get_genesis_utxo(index).await?
Box::default()
};
let input = TransactionInput { transaction, index };
let TransactionOutput {
contract, instance, ..
} = client.get_input_utxo(&input).await?;
let digest = starstream_ledger::parse_digest(&contract)
.with_context(|| format!("failed to parse `{contract}` as multibase multihash"))?;
let wasm = client.get_contract_wasm(digest).await?;
let (resolve, world) = decode_component(&wasm)?;
let world = &resolve.worlds[world];
let ty = world
Expand Down Expand Up @@ -529,10 +585,7 @@ async fn main() -> anyhow::Result<()> {
let ty = wasm_wave::value::resolve_wit_func_type(&resolve, &ty)
.context("failed to resolve method type")?;
let args = encode_args(&ty, args)?;
let digest = Sha256::digest(&wasm).into();
let rx = client
.call_utxo_method(&digest, &instance, &method, &methods, &storage, &args)
.await?;
let rx = client.call_utxo_method(&input, &method, &args).await?;
write_results(&ty, rx).await
}
}
Expand Down
52 changes: 42 additions & 10 deletions starstream-ledger-cli/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ async fn cli() {
.expect("failed to handle HTTP");
let ledger = tokio::spawn(ledger);

let wasm = NamedTempFile::new().unwrap();
fs::write(&wasm, &*SCORE_WASM)
let score_wasm = NamedTempFile::new().unwrap();
fs::write(&score_wasm, &*SCORE_WASM)
.await
.with_context(|| format!("failed to write Wasm to `{}`", wasm.path().display()))
.with_context(|| format!("failed to write Wasm to `{}`", score_wasm.path().display()))
.unwrap();

let digest = run_cli(["digest", &wasm.path().to_string_lossy()])
let score_digest = run_cli(["digest", &score_wasm.path().to_string_lossy()])
.await
.unwrap();
let digest = str::from_utf8(&digest)
let score_digest = str::from_utf8(&score_digest)
.expect("contract digest is not valid UTF-8")
.trim_end();

Expand All @@ -109,15 +109,15 @@ async fn cli() {
"--output-transaction",
&tx_file.path().to_string_lossy(),
"--import",
&wasm.path().to_string_lossy(),
digest,
&score_wasm.path().to_string_lossy(),
score_digest,
"example",
])
.await
.unwrap();
assert_eq!(stdout, b"()\n");
let tx = fs::read(&tx_file).await.unwrap();
let envelope = assert_score_transaction(&tx, digest);
let envelope = assert_score_transaction(&tx, score_digest);

let stdout = run_cli(["transaction", "show", &tx_file.path().to_string_lossy()])
.await
Expand Down Expand Up @@ -170,7 +170,7 @@ async fn cli() {
NETWORK,
"--nonce",
"1",
&wasm.path().to_string_lossy(),
&score_wasm.path().to_string_lossy(),
])
.await
.unwrap();
Expand Down Expand Up @@ -200,7 +200,39 @@ async fn cli() {
.unwrap();
assert_eq!(stdout, b"()\n");
let tx = fs::read(&tx_file).await.unwrap();
assert_score_transaction(&tx, digest);
assert_score_transaction(&tx, score_digest);

let require_hash_preimage_wasm = NamedTempFile::new().unwrap();
fs::write(&require_hash_preimage_wasm, &*REQUIRE_HASH_PREIMAGE_WASM)
.await
.with_context(|| {
format!(
"failed to write Wasm to `{}`",
require_hash_preimage_wasm.path().display()
)
})
.unwrap();
let stdout = run_cli([
"--url",
&format!("http://{addr}"),
"contract",
"script",
"call",
"--network",
NETWORK,
"--simulate",
&format!(
"sha256:{}",
hex::encode(Sha256::digest(&*REQUIRE_HASH_PREIMAGE_WASM))
),
"--import",
&require_hash_preimage_wasm.path().to_string_lossy(),
"create-hash",
"42",
])
.await
.unwrap();
assert_eq!(stdout, b"(0)\n");

shutdown.notify_one();
ledger.await.expect("ledger task panicked");
Expand Down
8 changes: 6 additions & 2 deletions starstream-ledger-node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ use ed25519_dalek::VerifyingKey;
use serde::Deserialize;
use sha2::{Digest as _, Sha256};
use starstream_ledger::client::runtime::{
Client, Contract, call_coordination_script, compile_component, new_contract,
Client, Contract, Ctx, call_coordination_script, compile_component, new_contract,
};
use starstream_ledger::server::Ledger;
use starstream_ledger::{TransactionInput, TransactionOutput, encode_digest, parse_digest};
use tokio::fs;
use tokio::signal;
use tokio::time::timeout;
use tracing::{error, info, warn};
use wasmtime::Store;
use wasmtime::component::Val;
use wasmtime_wizer::Wizer;

Expand Down Expand Up @@ -144,12 +145,13 @@ async fn build_genesis(
)
.await?;
let contract = Contract {
contract,
contract: Some(contract),
wasm: wasm.clone(),
};
contracts.insert(digest, contract.clone());
contract
};
let contract = contract.context("contract was not compiled")?;
let script = contract.get_coordination_script(&script)?;
let ty = script.ty();
let mut params = Vec::with_capacity(ty.params().len());
Expand All @@ -165,6 +167,7 @@ async fn build_genesis(
ensure!(args.next().is_none(), "trailing arguments");
let mut results = vec![Val::Bool(false); ty.results().len()];
let tx = call_coordination_script(
&mut Store::new(engine, Ctx::default()),
&GenesisClient(&imported),
&wizer,
&contract,
Expand All @@ -173,6 +176,7 @@ async fn build_genesis(
&mut contracts,
params,
&mut results,
&mut Vec::default(),
)
.await?;
outputs.extend(tx.outputs);
Expand Down
Loading
Loading