Skip to content

Commit 1b9d609

Browse files
Merge pull request #30 from hyperledger-solang/develop
feat(soroban): support compile, deploy and invoke
2 parents 42dbbf1 + 2f7dbb6 commit 1b9d609

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+9644
-565
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ flycheck_*.el
148148
*.tif
149149

150150
# Scalable Vector Graphics
151-
*.svg
152151
*.svgz
153152

154153
# Portable Document Format
@@ -507,4 +506,4 @@ $RECYCLE.BIN/
507506

508507
package-lock.json
509508

510-
packages/_generated/*/src
509+
packages/_generated/*/src

DeployAndRun1.png

1.38 MB
Loading

Dockerfile

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Start from a rust base image
2-
FROM rust:1.83.0 as base
2+
FROM rust:1.86.0 as base
33

44
# Set the current directory
55
WORKDIR /app
@@ -20,13 +20,16 @@ RUN cargo install cargo-make
2020
RUN apt-get --yes update
2121
RUN apt-get --yes upgrade
2222
ENV NVM_DIR /usr/local/nvm
23-
ENV NODE_VERSION v18.16.1
23+
ENV NODE_VERSION v22.14.0
2424
RUN mkdir -p /usr/local/nvm && apt-get update && echo "y" | apt-get install curl
2525
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
2626
RUN /bin/bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION && nvm use --delete-prefix $NODE_VERSION"
2727
ENV NODE_PATH $NVM_DIR/versions/node/$NODE_VERSION/bin
2828
ENV PATH $NODE_PATH:$PATH
2929

30+
# Create public directory if it doesn't exist
31+
RUN mkdir -p /app/packages/frontend/public
32+
3033
# Install dependencies
3134
RUN cargo make deps-wasm
3235
RUN cargo make deps-npm
@@ -35,6 +38,7 @@ RUN cargo make deps-npm
3538
RUN cargo make build-server
3639
RUN cargo make build-bindings
3740
RUN cargo make build-app
41+
RUN cargo make build-frontend
3842
RUN cargo make build-backend
3943

4044

@@ -45,8 +49,21 @@ FROM nestybox/ubuntu-jammy-systemd-docker:latest
4549

4650
# Copy the built files
4751
COPY --from=builder /app/packages/app/dist /app/packages/app/dist
52+
COPY --from=builder /app/packages/frontend/.next /app/packages/frontend/.next
53+
COPY --from=builder /app/packages/frontend/public /app/packages/frontend/public
54+
COPY --from=builder /app/packages/frontend/package.json /app/packages/frontend/package.json
4855
COPY --from=builder /app/target/release/backend /app/target/release/backend
4956

57+
# Install Node.js in the final image for running Next.js
58+
RUN apt-get update && apt-get install -y curl
59+
ENV NVM_DIR /usr/local/nvm
60+
ENV NODE_VERSION v22.14.0
61+
RUN mkdir -p /usr/local/nvm
62+
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
63+
RUN /bin/bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION && nvm use --delete-prefix $NODE_VERSION"
64+
ENV NODE_PATH $NVM_DIR/versions/node/$NODE_VERSION/bin
65+
ENV PATH $NODE_PATH:$PATH
66+
5067
# Startup scripts
5168
COPY sysbox/on-start.sh /usr/bin
5269
RUN chmod +x /usr/bin/on-start.sh

Makefile.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ skip_core_tasks = true
44

55
[tasks.deps-wasm]
66
script = '''
7-
cargo install -f wasm-bindgen-cli --version 0.2.99
7+
cargo install wasm-bindgen-cli
88
'''
99

1010
[tasks.deps-npm]
@@ -14,7 +14,7 @@ npm install
1414

1515
[tasks.deps-docker]
1616
script = '''
17-
docker pull ghcr.io/hyperledger/solang@sha256:8776a9bd756664f7bf8414710d1a799799bf6fedc1c8f9f0bda17e76749dea7a
17+
docker pull ghcr.io/hyperledger-solang/solang:latest
1818
'''
1919

2020
[tasks.deps]
@@ -111,14 +111,14 @@ dependencies = ["test-backend", "test-app", "test-frontend"]
111111

112112
[tasks.docker-build]
113113
script = '''
114-
docker build -t solang-playground .
114+
docker build -t solang-playground .
115115
'''
116116

117117
[tasks.docker-run]
118118
script = '''
119119
docker run \
120120
--runtime=sysbox-runc \
121-
--name playground \
121+
--name playground-makee \
122122
--detach \
123123
--volume /tmp:/tmp \
124124
--publish 9000:9000 \
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"ids":{"Standalone Network ; February 2017":"CCRITMVLLETIV3VGOA4AL74UE3UG7DI4N652J32YNLALGJ3ARYXRGE3F"}}

crates/backend/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ regex = "1.10"
2222
typescript-type-def = "0.5"
2323
actix-web = "4.5"
2424
actix-files = "0.6"
25+
actix-cors = "0.7.1"

crates/backend/src/main.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use actix_web::{
1111
};
1212

1313
use backend::{route_compile, Opts};
14+
use actix_cors::Cors;
1415

1516
pub struct FrontendState {
1617
pub frontend_folder: String,
@@ -51,15 +52,23 @@ async fn main() -> std::io::Result<()> {
5152
let frontend_folder = opts.frontend_folder.clone();
5253

5354
let mut app = App::new()
54-
.service(web::resource("/health").to(health))
55-
// Enable GZIP compression
56-
.wrap(middleware::Compress::default())
57-
.wrap(
58-
DefaultHeaders::new()
59-
.add(("Cross-Origin-Opener-Policy", "same-origin"))
60-
.add(("Cross-Origin-Embedder-Policy", "require-corp")),
61-
)
62-
.route("/compile", post().to(route_compile));
55+
.wrap(
56+
Cors::default()
57+
.allow_any_origin() // allows all origins, including localhost
58+
.allow_any_method() // allows GET, POST, etc.
59+
.allow_any_header() // allows all headers
60+
.supports_credentials() // allow cookies/auth if needed
61+
.max_age(3600), // preflight cache duration
62+
)
63+
.service(web::resource("/health").to(health))
64+
// Enable GZIP compression
65+
.wrap(middleware::Compress::default())
66+
.wrap(
67+
DefaultHeaders::new()
68+
.add(("Cross-Origin-Opener-Policy", "same-origin"))
69+
.add(("Cross-Origin-Embedder-Policy", "require-corp")),
70+
)
71+
.route("/compile", post().to(|body| route_compile(body)));
6372

6473
// Serve frontend files if configured via CLI
6574
match frontend_folder {

crates/backend/src/services/sandbox.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use tokio::process::Command;
1414
use crate::services::{CompilationRequest, CompilationResult};
1515

1616
const TIMEOUT: Duration = Duration::from_secs(60);
17-
const DOCKER_IMAGE_BASE_NAME: &str = "ghcr.io/hyperledger/solang";
17+
// const DOCKER_IMAGE_BASE_NAME: &str = "ghcr.io/hyperledger-solang/solang@sha256:e6f687910df5dd9d4f5285aed105ae0e6bcae912db43e8955ed4d8344d49785d";
18+
const DOCKER_IMAGE_BASE_NAME: &str = "ghcr.io/hyperledger-solang/solang:latest";
1819
const DOCKER_WORKDIR: &str = "/builds/contract/";
1920
const DOCKER_OUTPUT: &str = "/playground-result";
2021

@@ -29,11 +30,12 @@ macro_rules! docker_command {
2930

3031
/// Builds the compile command using solang docker image
3132
pub fn build_compile_command(input_file: &Path, output_dir: &Path) -> Command {
33+
println!("ip file: {:?}\nop dir: {:?}", input_file, output_dir);
3234
// Base docker command
3335
let mut cmd = docker_command!(
3436
"run",
3537
"--detach",
36-
"--rm",
38+
// "--rm",
3739
"-it",
3840
"--cap-drop=ALL",
3941
"--cap-add=DAC_OVERRIDE",
@@ -65,15 +67,12 @@ pub fn build_compile_command(input_file: &Path, output_dir: &Path) -> Command {
6567
cmd.arg("--volume").arg(&mount_output_dir);
6668

6769
// Using the solang image
68-
cmd.arg(format!(
69-
"{}@sha256:8776a9bd756664f7bf8414710d1a799799bf6fedc1c8f9f0bda17e76749dea7a",
70-
DOCKER_IMAGE_BASE_NAME
71-
));
70+
cmd.arg(DOCKER_IMAGE_BASE_NAME);
7271

7372
// Building the compile command
7473
let remove_command = format!("rm -rf {}*.wasm {}*.contract", DOCKER_OUTPUT, DOCKER_OUTPUT);
7574
let compile_command = format!(
76-
"solang compile --target polkadot -o /playground-result {} > /playground-result/stdout.log 2> /playground-result/stderr.log",
75+
"solang compile --target soroban -o /playground-result {} > /playground-result/stdout.log 2> /playground-result/stderr.log",
7776
file_name
7877
);
7978
let sh_command = format!("{} && {}", remove_command, compile_command);
@@ -102,7 +101,9 @@ impl Sandbox {
102101

103102
fs::set_permissions(&output_dir, PermissionsExt::from_mode(0o777))
104103
.context("failed to set output permissions")?;
105-
104+
105+
File::create(&input_file).context("failed to create input file")?;
106+
106107
Ok(Sandbox {
107108
scratch,
108109
input_file,
@@ -115,14 +116,15 @@ impl Sandbox {
115116
self.write_source_code(&req.source)?;
116117

117118
let command = build_compile_command(&self.input_file, &self.output_dir);
118-
println!("Executing command: \n{:#?}", command);
119+
// println!("Executing command: \n{:#?}", command);
119120

120121
let output = run_command(command)?;
122+
println!("out: {:?}", output);
121123
let file = fs::read_dir(&self.output_dir)
122124
.context("failed to read output directory")?
123125
.flatten()
124126
.map(|entry| entry.path())
125-
.find(|path| path.extension() == Some(OsStr::new("contract")));
127+
.find(|path| path.extension() == Some(OsStr::new("wasm")));
126128

127129
// The file `stdout.log` is in the same directory as the contract file
128130
let compile_log_stdout_file_path = fs::read_dir(&self.output_dir)
@@ -187,9 +189,16 @@ impl Sandbox {
187189

188190
/// A helper function to write the source code to the input file
189191
fn write_source_code(&self, code: &str) -> Result<()> {
192+
println!("writing to {:?}", self.input_file);
190193
fs::write(&self.input_file, code).context("failed to write source code")?;
194+
match fs::read_to_string(&self.input_file) {
195+
Ok(content) => println!("Successfully read: {:?}", content),
196+
Err(e) => eprintln!("Error reading file: {}", e),
197+
}
191198
fs::set_permissions(&self.input_file, PermissionsExt::from_mode(0o777))
192199
.context("failed to set source permissions")?;
200+
let s: String = code.chars().take(40).collect();
201+
println!("Code: {:?}", s);
193202
println!("Wrote {} bytes of source to {}", code.len(), self.input_file.display());
194203
Ok(())
195204
}
@@ -204,7 +213,7 @@ fn read(path: &Path) -> Result<Option<Vec<u8>>> {
204213
};
205214
let mut f = BufReader::new(f);
206215
let metadata = fs::metadata(path).expect("failed to read metadata");
207-
216+
println!("meta: {:?}", metadata);
208217
let mut buffer = vec![0; metadata.len() as usize];
209218
f.read_exact(&mut buffer).expect("buffer overflow");
210219
Ok(Some(buffer))
@@ -219,14 +228,15 @@ async fn run_command(mut command: Command) -> Result<std::process::Output> {
219228
use std::os::unix::process::ExitStatusExt;
220229

221230
let timeout = TIMEOUT;
222-
println!("executing command!");
231+
println!("executing command: {:?}", command);
223232
let output = command.output().await.context("failed to start compiler")?;
224233

225234
let stdout = String::from_utf8_lossy(&output.stdout);
226235
let id = stdout.lines().next().context("missing compiler ID")?.trim();
227236
let stderr = &output.stderr;
228-
237+
229238
let mut command = docker_command!("wait", id);
239+
println!("ID: {:?}\nwait: {:?}", id, command);
230240

231241
let timed_out = match tokio::time::timeout(timeout, command.output()).await {
232242
Ok(Ok(o)) => {
@@ -239,17 +249,19 @@ async fn run_command(mut command: Command) -> Result<std::process::Output> {
239249
};
240250

241251
let mut command = docker_command!("logs", id);
252+
println!("logs: {:?}", command);
242253
let mut output = command.output().await.context("failed to get output from compiler")?;
243-
244-
let mut command = docker_command!(
245-
"rm", // Kills container if still running
246-
"--force", id
247-
);
248-
command.stdout(std::process::Stdio::null());
249-
command.status().await.context("failed to remove compiler")?;
254+
println!("op: {:?}", output);
255+
// let mut command = docker_command!(
256+
// "rm", // Kills container if still running
257+
// "--force", id
258+
// );
259+
// println!("rm: {:?}", command);
260+
// command.stdout(std::process::Stdio::null());
261+
// command.status().await.context("failed to remove compiler")?;
250262

251263
let code = timed_out.context("compiler timed out")?;
252-
264+
println!("timedout: {:?}", code);
253265
output.status = code;
254266
output.stderr = stderr.to_owned();
255267

crates/browser/tests/server_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ mod tests {
1313

1414
use super::*;
1515

16-
const INITIALIZE_REQUEST: &str = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"clientInfo":{"name":"demo-language-client"},"capabilities":{},"rootUri":null}}"#;
17-
const INITIALIZE_EXPECTED_RESPONSE: &str = r#"{"jsonrpc":"2.0","result":{"capabilities":{"completionProvider":{"resolveProvider":false,"triggerCharacters":["."]},"declarationProvider":true,"definitionProvider":true,"documentFormattingProvider":true,"executeCommandProvider":{"commands":[]},"hoverProvider":true,"implementationProvider":true,"referencesProvider":true,"renameProvider":true,"signatureHelpProvider":{},"textDocumentSync":2,"typeDefinitionProvider":true,"workspace":{"workspaceFolders":{"changeNotifications":true,"supported":true}},"workspaceSymbolProvider":true}},"id":1}"#;
16+
const INITIALIZE_REQUEST: &'static str = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"clientInfo":{"name":"demo-language-client"},"capabilities":{},"rootUri":null}}"#;
17+
const INITIALIZE_EXPECTED_RESPONSE: &'static str = r#"{"jsonrpc":"2.0","result":{"capabilities":{"completionProvider":{"resolveProvider":false,"triggerCharacters":["."]},"declarationProvider":true,"definitionProvider":true,"documentFormattingProvider":true,"executeCommandProvider":{"commands":[]},"hoverProvider":true,"implementationProvider":true,"referencesProvider":true,"renameProvider":true,"signatureHelpProvider":{},"textDocumentSync":2,"typeDefinitionProvider":true,"workspace":{"workspaceFolders":{"changeNotifications":true,"supported":true}},"workspaceSymbolProvider":true}},"id":1}"#;
1818

1919
const INITALIZED: &str = r#"{"jsonrpc":"2.0","method":"initialized","params":{}}"#;
2020

21-
const TEXTDOCUMENT_DIDOPEN: &str = r#" {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"inmemory://demo.js","languageId":"solidity","version":0,"text":" // SPDX-License-Identifier: MIT\n pragma solidity >=0.6.12 <0.9.0;\n contract HelloWorld {\n /**\n * @dev Prints Hello World string\n */\n function print() public pure returns (string memory) {\n return \"Hello World!\";\n }\n }\n"}}}"#;
21+
const TEXTDOCUMENT_DIDOPEN: &'static str = r#" {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"inmemory://demo.js","languageId":"solidity","version":0,"text":" // SPDX-License-Identifier: MIT\n pragma solidity >=0.6.12 <0.9.0;\n contract HelloWorld {\n /**\n * @dev Prints Hello World string\n */\n function print() public pure returns (string memory) {\n return \"Hello World!\";\n }\n }\n"}}}"#;
2222

2323
const DIAGNOSTIC_REQUEST: &str = r#"{"jsonrpc":"2.0","id":3,"method":"textDocument/diagnostic","params":{"textDocument":{"uri":"inmemory://demo.js","languageId":"solidity","version":0,"text":" // SPDX-License-Identifier: MIT\n pragma solidity >=0.6.12 <0.9.0;\n contract HelloWorld {\n /**\n * @dev Prints Hello World string\n */\n function print() public pure returns (string memory) {\n return \"Hello World!\";\n }\n }\n"}}}"#;
2424

crates/solang/solang-parser/src/helpers/loc.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,19 @@ impl<T: CodeLocation> OptionalCodeLocation for Vec<T> {
4545
}
4646
}
4747

48-
impl<T: ?Sized + OptionalCodeLocation> OptionalCodeLocation for &T {
48+
impl<'a, T: ?Sized + OptionalCodeLocation> OptionalCodeLocation for &'a T {
4949
fn loc_opt(&self) -> Option<Loc> {
5050
(**self).loc_opt()
5151
}
5252
}
5353

54-
impl<T: ?Sized + OptionalCodeLocation> OptionalCodeLocation for &mut T {
54+
impl<'a, T: ?Sized + OptionalCodeLocation> OptionalCodeLocation for &'a mut T {
5555
fn loc_opt(&self) -> Option<Loc> {
5656
(**self).loc_opt()
5757
}
5858
}
5959

60-
impl<T: ?Sized + ToOwned + OptionalCodeLocation> OptionalCodeLocation for Cow<'_, T> {
60+
impl<'a, T: ?Sized + ToOwned + OptionalCodeLocation> OptionalCodeLocation for Cow<'a, T> {
6161
fn loc_opt(&self) -> Option<Loc> {
6262
(**self).loc_opt()
6363
}
@@ -160,19 +160,19 @@ impl CodeLocation for Loc {
160160
}
161161
}
162162

163-
impl<T: ?Sized + CodeLocation> CodeLocation for &T {
163+
impl<'a, T: ?Sized + CodeLocation> CodeLocation for &'a T {
164164
fn loc(&self) -> Loc {
165165
(**self).loc()
166166
}
167167
}
168168

169-
impl<T: ?Sized + CodeLocation> CodeLocation for &mut T {
169+
impl<'a, T: ?Sized + CodeLocation> CodeLocation for &'a mut T {
170170
fn loc(&self) -> Loc {
171171
(**self).loc()
172172
}
173173
}
174174

175-
impl<T: ?Sized + ToOwned + CodeLocation> CodeLocation for Cow<'_, T> {
175+
impl<'a, T: ?Sized + ToOwned + CodeLocation> CodeLocation for Cow<'a, T> {
176176
fn loc(&self) -> Loc {
177177
(**self).loc()
178178
}

0 commit comments

Comments
 (0)