Skip to content

Commit 1fb111e

Browse files
committed
Expand milestone_2 test
1 parent 7dd50c8 commit 1fb111e

File tree

6 files changed

+111
-28
lines changed

6 files changed

+111
-28
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ solang-forge-fmt = { version = "0.2.0", optional = true }
7272
# build to work.
7373
ethers-core = { version = "2.0.10", optional = true }
7474
soroban-sdk = { version = "23.0.0-rc.2.2", features = ["testutils"], optional = true }
75+
anyhow = "1.0"
76+
brotli2 = "0.3"
7577

7678
[dev-dependencies]
7779
num-derive = "0.4"
@@ -93,7 +95,6 @@ ink_primitives = "5.0.0"
9395
wasm_host_attr = { path = "tests/wasm_host_attr" }
9496
num-bigint = { version = "0.4", features = ["rand", "serde"]}
9597
strip-ansi-escapes = "0.2"
96-
anyhow = "1.0"
9798

9899

99100
[package.metadata.docs.rs]

integration/stylus/milestone_2.sol

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity >=0.8.0;
33

4-
contract T {
5-
function test() public view returns (uint256) {
6-
return 1;
7-
}
4+
interface ArbWasm {
5+
/// @notice Activate a wasm program
6+
/// @param program the program to activate
7+
/// @return version the stylus version the program was activated against
8+
/// @return dataFee the data fee paid to store the activated program
9+
function activateProgram(
10+
address program
11+
) external payable returns (uint16 version, uint256 dataFee);
812
}
913

1014
contract C {
@@ -44,4 +48,24 @@ contract C {
4448
}
4549
return (a, b);
4650
}
51+
52+
function test3() public payable {
53+
Greeter greeter = new Greeter();
54+
print("greeter = {}".format(address(greeter)));
55+
56+
ArbWasm arbWasm = ArbWasm(address(0x71));
57+
(uint16 version, uint256 dataFee) = arbWasm.activateProgram{
58+
value: msg.value
59+
}(address(greeter));
60+
print("version = {}".format(version));
61+
print("dataFee = {}".format(dataFee));
62+
63+
greeter.greet();
64+
}
65+
}
66+
67+
contract Greeter {
68+
function greet() public view {
69+
print("Hello from 0x{}!".format(this));
70+
}
4771
}

src/emit/stylus/cargo_stylus.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//! This file contains two functions similar to ones from `cargo-stylus`'s codebase, which is
2+
//! distributed under the terms of both the MIT license and the Apache License (Version 2.0).
3+
4+
use anyhow::Context;
5+
6+
// smoelius: `compress_wasm` is based on the function of the same name here:
7+
// https://github.com/OffchainLabs/cargo-stylus/blob/5c520876d54594d9ca93cf017cb966075b4f4ca5/main/src/project.rs#L381
8+
pub fn compress_wasm(wasm: &[u8]) -> anyhow::Result<(Vec<u8>, Vec<u8>)> {
9+
use brotli2::read::BrotliEncoder;
10+
use std::io::Read;
11+
12+
/// Maximum brotli compression level used for Stylus contracts.
13+
const BROTLI_COMPRESSION_LEVEL: u32 = 11;
14+
15+
/// EOF prefix used in Stylus compressed WASMs on-chain
16+
const EOF_PREFIX_NO_DICT: &str = "EFF00000";
17+
18+
let mut compressor = BrotliEncoder::new(&*wasm, BROTLI_COMPRESSION_LEVEL);
19+
let mut compressed_bytes = vec![];
20+
compressor
21+
.read_to_end(&mut compressed_bytes)
22+
.context("failed to compress WASM bytes")?;
23+
24+
let mut contract_code = hex::decode(EOF_PREFIX_NO_DICT).unwrap();
25+
contract_code.extend(compressed_bytes);
26+
27+
Ok((wasm.to_vec(), contract_code))
28+
}
29+
30+
// smoelius: `contract_deployment_calldata` is based on the function of the same name here:
31+
// https://github.com/OffchainLabs/cargo-stylus/blob/5c520876d54594d9ca93cf017cb966075b4f4ca5/main/src/deploy/mod.rs#L305
32+
pub fn contract_deployment_calldata(code: &[u8]) -> Vec<u8> {
33+
let code_len: [u8; 32] = u256_from_usize(code.len());
34+
let mut deploy: Vec<u8> = vec![];
35+
deploy.push(0x7f); // PUSH32
36+
deploy.extend(code_len);
37+
deploy.push(0x80); // DUP1
38+
deploy.push(0x60); // PUSH1
39+
deploy.push(42 + 1); // prelude + version
40+
deploy.push(0x60); // PUSH1
41+
deploy.push(0x00);
42+
deploy.push(0x39); // CODECOPY
43+
deploy.push(0x60); // PUSH1
44+
deploy.push(0x00);
45+
deploy.push(0xf3); // RETURN
46+
deploy.push(0x00); // version
47+
deploy.extend(code);
48+
deploy
49+
}
50+
51+
fn u256_from_usize(x: usize) -> [u8; 32] {
52+
let mut bytes = [0u8; 32];
53+
bytes[32 - std::mem::size_of_val(&x)..].copy_from_slice(&x.to_be_bytes());
54+
bytes
55+
}

src/emit/stylus/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use inkwell::module::{Linkage, Module};
1111
use inkwell::values::{BasicMetadataValueEnum, BasicValue, FunctionValue, IntValue, PointerValue};
1212
use inkwell::AddressSpace;
1313

14+
mod cargo_stylus;
1415
mod storage;
1516
mod target;
1617

src/emit/stylus/target.rs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,14 @@ impl<'a> TargetRuntime<'a> for StylusTarget {
675675

676676
let created_contract = &bin.ns.contracts[contract_no];
677677

678-
let code = created_contract.emit(bin.ns, bin.options, contract_no);
678+
let code_without_metadata_uncompressed =
679+
created_contract.emit(bin.ns, bin.options, contract_no);
680+
681+
let (_, code_without_metadata) =
682+
super::cargo_stylus::compress_wasm(&code_without_metadata_uncompressed)
683+
.expect("failed to compress wasm");
684+
685+
let code = super::cargo_stylus::contract_deployment_calldata(&code_without_metadata);
679686

680687
let code_ptr =
681688
bin.emit_global_string(&format!("binary_{}_code", created_contract.id), &code, true);
@@ -730,7 +737,7 @@ impl<'a> TargetRuntime<'a> for StylusTarget {
730737
let zero_ptr = bin.build_alloca(function, bin.address_type(), "zero_ptr");
731738

732739
bin.builder.build_store(zero_ptr, zero_address).unwrap();
733-
let cmp_result = bin
740+
let is_zero = bin
734741
.builder
735742
.build_call(
736743
bin.module.get_function("__memcmp").unwrap(),
@@ -755,25 +762,10 @@ impl<'a> TargetRuntime<'a> for StylusTarget {
755762
.into_int_value();
756763

757764
// __memcmp returns true (1) if memory regions are equal, false (0) if not equal
758-
// We want success to be 0 if address equals zero (creation failed), 1 if address is non-zero (creation succeeded)
759-
// So we need to invert the result: if __memcmp returns 1 (equal), we want success to be 0 (failure)
760-
let success_value = bin
761-
.builder
762-
.build_select(
763-
bin.builder
764-
.build_int_compare(
765-
IntPredicate::EQ,
766-
cmp_result,
767-
bin.context.bool_type().const_int(1, false),
768-
"address_is_zero",
769-
)
770-
.unwrap(),
771-
bin.context.i32_type().const_zero(),
772-
bin.context.i32_type().const_int(1, false),
773-
"success_value",
774-
)
775-
.unwrap();
776-
*success = success_value.into();
765+
// We want success to be 1 if address equals zero (creation failed), 0 if address is non-zero (creation succeeded)
766+
// So we can use the __memcmp result directly.
767+
768+
*success = is_zero.into();
777769
}
778770
}
779771

tests/stylus_tests/milestone_2.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@
66
//! - <https://book.getfoundry.sh/cast/>
77
#![warn(clippy::pedantic)]
88

9-
use crate::{call, deploy, MUTEX};
10-
use std::path::PathBuf;
9+
use crate::{call, deploy, send, MUTEX};
10+
use std::{io::Write, path::PathBuf};
1111

1212
#[test]
1313
fn milestone_2() {
14+
writeln!(
15+
std::io::stderr(),
16+
"If you run the `milestone_2` test twice, it will fail the second time because the \
17+
contract `Greeter` cannot be activated twice.",
18+
)
19+
.unwrap();
20+
1421
let _lock = MUTEX.lock();
1522
let (tempdir, address) = deploy(
1623
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("integration/stylus/milestone_2.sol"),
@@ -30,6 +37,9 @@ fn milestone_2() {
3037

3138
stdout = call(dir, &address, ["test2()(uint256,uint256)"]).unwrap();
3239
println!("{}", stdout);
40+
41+
stdout = send(dir, &address, ["test3()", "--value=1000000000000000000"]).unwrap();
42+
println!("{}", stdout);
3343
}
3444

3545
fn label(stdout: &str) -> String {

0 commit comments

Comments
 (0)