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
invoke: add --event and --event-file flags #137
Conversation
Add new event-related flags to the invoke subcommand: * --events: enable printing host/debug events * --events-file: write events to a file instead of stdout
The dependency of serve on invoke via a file/pipe seems unnecessary, and unintuitive, and breaks the "these are separate runtimes" model. I don't think someone running If a transaction is submitted to But if a contract is invoked via cc @paulbellamy Wdyt? |
let event_str = match event { | ||
HostEvent::Contract(e) => serde_json::to_string(&e).unwrap(), | ||
HostEvent::Debug(e) => format!("{}", e), | ||
}; | ||
writeln!(out, "{}", event_str).map_err(|e| Error::CannotWriteEvent { error: e })?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it will produce a file that contains invalid JSON, because debug events are not being outputted as JSON, so I don't think this file will be very usable.
It's quite unclear what the use of writing the events to file from the invoke command, which is a command intended just for a human to use. I think we could leave it out of the CLI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote the intention in the PR's description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should transform the debug events to JSON though
@2opremio if a developer doesn't supply an |
@paulbellamy @2opremio The intent with the invoke command was that it could also invoke contracts on remote hosts by building a tx and submitting it to it. Originally that remote host was just going to be a Horizon. But if you'd like the invoke command to invoke a transaction in an RPC server I think that makes sense too. That's how I think it would make the most sense to integrate invoke with a running RPC server (or the serve command which is just an RPC server emulated). In this situation the runtime would be inside the serve, so the events would be generated inside the serve. Does that achieve what you're wanting to do? |
I think that's the fundamental disagreement here. I think they should be the same "runtime". If you have an rpc server running against a network, and submit a new txn there via "invoke", then you'd expect that to be interoperable. I don't see why the sandbox should be different.
tl;dr is I super disagree with all of that. Edit: Let me elaborate... The way I think of the soroban dev tooling is MVC-inspired, something like this, where grey arrows indicate a user's choice of setup: (@leighmcculloch, I think we did a diagram similar to this at one point, but can't find it now) From that perspective, sandbox should behave similar to any other stateful network. If you use For sandbox, this is really important to help with locally testing your dapp. Imagine you're writing a crowdfunding campaign dapp. The workflow I'm using to test soroban-example-dapp from the user-facing side is: # Build/deploy/initialize the contract.
# IRL, this is done via: https://github.com/stellar/soroban-example-dapp/blob/main/initialize.sh,
# but I've expanded it here to show what is going on.
# Build the crowdfund contract
$ cargo build --release --target wasm32-unknown-unknown
# Deploy the crowdfund contract
$ soroban-cli deploy --id 0 \
--wasm target/wasm32-unknown-unknown/release/soroban_crowdfund_contract.wasm
# Initialize the crowdfund contract
$ deadline="$(($(date +"%s") + 86400))"
$ soroban-cli invoke --id 0 \
--fn initialize \
--arg-xdr "$admin" \
--arg "$deadline" \
--arg "1000000000" \
--arg '[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]'
# Launch the cli serve so I have a jsonrpc server backed
# by the sandbox (for the web UI to talk to)
$ soroban-cli serve
# Launch the web app UI http server (in a separate terminal)
$ yarn dev
# Poke around in the web UI, and do a donation. A this point I
# have a working crowdfund contract in the sandbox, because I've
# called the "initialize" method on it. `serve` and the previous
# `deploy` and `invoke` all share the same context via `ledger.json`.
#
# In testing, I could:
# - Make a donation via the web UI, then check the
# new balance via `cli invoke`
# or
# - Conclude the crowdfund via `cli invoke` then observe
# the new status via the web UI, to check the frontend handling. Whether (internally) that is implemented via having the cli subcommands do a pseudo-jsonrpc call to Side-note: This mental-model is why I feel that eventually having |
@@ -42,6 +47,12 @@ pub struct Cmd { | |||
/// File to persist ledger state | |||
#[clap(long, parse(from_os_str), default_value(".soroban/ledger.json"))] | |||
ledger_file: std::path::PathBuf, | |||
/// Output file to write events | |||
#[clap(long, parse(from_os_str))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[clap(long, parse(from_os_str))] | |
#[clap(long, parse(from_os_str), default_value(".soroban/events.json"))] |
wdyt?
Got it, interesting. I think a developer will see the events file like a log file. For example, debug events (called logs in the SDK) are logged as a non-roundtrippable string because the intent is for the developer to write this code:
and to see a human readable formatted string, like this:
To make the output roundtrippable we'd have to output the debug event as a JSON object which would make logs not human readable as intended. I think this means at the very least we need to separate debug events from contract and system events when configuring output destinations, so that we can continue to output human readable logs without needing them to be roundtrippable. The second thing is that when I look at the CLI interface and see an events file it isn't intuitive to me that it is a communication mechanism. Even the ledger.json file is dubious to describe as a communication mechanism. From the perspective of the invoke I would describe it as just an input and output. It's also worth noting that having two applications read and write from the same file seems like it will have its challenges with corruption, etc. Especially when people on a variety of platforms are using this tool (Windows, Mac, Linux, etc). I think it is very reasonable to expect that the invoke command can affect the serve runtime and to generate events there, but I think the way it should do that is by submitting a transaction to the serve http endpoint, and then streaming back the results and events from the RPC server in the same way it will do so when invoking a contract with a real remote RPC server. i.e. The integration between cli commands and the serve command should match the integration between cli commands and a real RPC server. The serve is after all just a local emulated version of the RPC server. We could maybe even make this seamless to the developer by writing the serve URL to a file in |
@leighmcculloch I think we broadly agree, just talking past each other here. re: having soroban-cli invoke do a jsonrpc request to serve if it is running. Seems... complicated to get reliable, and like it could break in strange ways. Doing an internal jsonrpc request seems like a hacky way to re-use code. But, if we need to in order to prevent data corruption, then fine. Maybe a better solution would be to share the implementation code, and have both processes use something like sqlite that would ensure consistency? Another dependency, though, which sucks. As it is now, I don't expect things in the |
This is the way that the dapp will communicate with the RPC server and the serve emulator, so we need a reliable http server in any case? |
Regarding pipes in general: Keep in mind that soroban-cli will be used on five targets, three operating systems across two architectures and pipes come with their own set of challenges such a deadlock, and may not behave the same on all operating systems. Would the pipe be where the soroban-cli sends the JSON-RPC request? That sounds fine if you prefer that, although I think http will present less issues, and I think this creates additional work unnecessarily. If the pipe will be a side channel for events only, this seems like a not ideal architecture, and again creates additional work unnecessarily. The serve already exposes a transaction endpoint, and we're already adding logic to invoke to send a transaction to FutureNet in #42, so in the minimal case where a developer points invoke at their local serve it's zero additional work on top of other work we already have planned. |
That was the idea, using the pipe as an one-way communication channel for events from If you feel strongly about using an http endpoint instead, that's fine, but please let's make a choice and move forward. We have already spent too much time on this. @leighmcculloch as I understand it, what you are suggesting is something like:
Please confirm. If that's not what you mean, please provide an explicit design and user-flow example. Having to deal with |
@2opremio That's not what I'm suggesting. I'm suggesting we build no side-channel for events. I'm suggesting we build what we plan to build already for invoking contracts on remote RPC servers, which serve is a local emulator for, i.e. #42. By invoking a contract on the remote RPC server (i.e. soroban-cli serve), events should naturally be generated within the soroban-cli serve runtime, and the invoke command should be able to subscribe to events like it would any remote RPC server or dapp. To break it down:
i.e. I'm suggesting we build nothing unique / bespoke for sandbox invoke with sandbox serve, and instead rely on features we already have to build for the dapp to serve integration. |
I don’t fully understand what needs to be done from your description. Do you mean that #39 shouldn’t be implemented at all? Please be clear on the mechanics of |
That's what I'm saying, we don't need to build anything bespoke for the sandbox version of invoke for use with serve. The remote URL version of invoke (#42) can be used to invoke contracts on #39 is a completely different issue afaik, as it will stream events from a remote rpc endpoint. |
|
What
Add new event-related flags to the invoke subcommand:
Why
soroban-cli serve
to subscribe to local events (a new, matching--events-file
flag will be added to tosorobab-cli serve
for that purpose). This is part of CLI: CLI Event Streaming Tool for local sandbox #39Known limitations
N/A