diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1c4041c..734064a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,9 @@ name: SubStream Contract Tests on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] env: CARGO_TERM_COLOR: always @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - + - name: Install Rust uses: dtolnay/rust-toolchain@stable with: @@ -29,9 +29,19 @@ jobs: find . -maxdepth 2 -name stellar -type f -exec sudo mv {} /usr/local/bin/ \; stellar --version + - name: Install Binaryen for WASM optimization + run: | + sudo apt-get update + sudo apt-get install -y binaryen + - name: Build Contract run: cargo build --target wasm32-unknown-unknown --release + - name: Build and Compress WASM + run: | + cd contracts/substream_contracts + make build-compressed + - name: Run Unit Tests run: cargo test @@ -41,9 +51,15 @@ jobs: - name: Run Code Coverage run: | cargo tarpaulin --out Lcov --output-dir coverage - + - name: Upload Coverage report uses: actions/upload-artifact@v3 with: name: code-coverage-report path: coverage/lcov.info + + - name: Upload Compressed WASM files + uses: actions/upload-artifact@v3 + with: + name: compressed-wasm + path: contracts/substream_contracts/target/compressed/ diff --git a/README.md b/README.md index 781168f..a8621f4 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ If the fan dislikes the content, they can cancel instantly and get their remaini ## Sybil Protection To prevent users from rapidly starting/stopping streams to "scrape" content, the protocol enforces a **minimum flow duration of 24 hours**. Once a stream is initiated, it cannot be canceled until the minimum duration has elapsed. This protects creators from abuse and ensures meaningful engagement. + - **cancel**: Subscriber stops the stream and refunds unspent tokens. - **subscribe_group**: User streams to a group channel with exactly 5 creators and percentage splits that sum to 100. - **collect_group**: Contract automatically splits each collected amount to all 5 creators based on configured percentages. @@ -63,3 +64,22 @@ To build the contract for Wasm: ```bash cargo build --target wasm32-unknown-unknown --release ``` + +### WASM Compression (Recommended for Deployment) + +To reduce deployment fees, use the automated WASM compression: + +```bash +cd contracts/substream_contracts +make build-compressed +``` + +Or use the compression script: + +```bash +./scripts/compress_wasm.sh +``` + +The compression system uses `wasm-opt` to optimize WASM binaries, typically reducing file sizes by 10-30%, which directly lowers deployment costs on Stellar. + +See [WASM_COMPRESSION.md](./WASM_COMPRESSION.md) for detailed documentation. diff --git a/WASM_COMPRESSION.md b/WASM_COMPRESSION.md new file mode 100644 index 0000000..d406d3e --- /dev/null +++ b/WASM_COMPRESSION.md @@ -0,0 +1,258 @@ +# WASM Compression for SubStream Protocol Contracts + +This document describes the WASM compression implementation for SubStream Protocol Contracts, designed to reduce deployment fees on the Stellar network by optimizing contract binary sizes. + +## Overview + +The WASM compression system uses `wasm-opt` from the Binaryen toolkit to apply aggressive optimizations to contract binaries, resulting in smaller WASM files that cost less to deploy on Stellar. + +## Features + +- **Aggressive Optimization**: Uses `wasm-opt -Oz` with additional optimization flags +- **Size Reporting**: Shows before/after file sizes and compression percentages +- **CI/CD Integration**: Automated compression in GitHub Actions +- **Flexible Configuration**: Customizable optimization levels and output directories +- **Multiple Contracts**: Handles all WASM files in the target directory + +## Tools Used + +- **Binaryen**: Provides `wasm-opt` for WASM optimization +- **Stellar CLI**: Builds the contracts to WASM +- **Make**: Automation of build and compression steps + +## Installation + +### Local Development + +1. Install Binaryen: + ```bash + # macOS + brew install binaryen + + # Ubuntu/Debian + sudo apt-get install binaryen + + # Other platforms + # Download from https://github.com/WebAssembly/binaryen/releases + ``` + +2. Verify installation: + ```bash + wasm-opt --version + ``` + +### CI/CD + +The GitHub Actions workflow automatically installs Binaryen for WASM optimization. + +## Usage + +### Command Line + +#### Using the Makefile (Recommended) + +```bash +# Build and compress in one step +cd contracts/substream_contracts +make build-compressed + +# Or build first, then compress +make build +make build-compressed +``` + +#### Using the Compression Script + +```bash +# Basic usage +./scripts/compress_wasm.sh + +# With custom options +./scripts/compress_wasm.sh \ + --contract-dir contracts/substream_contracts \ + --output-dir target/compressed \ + --optimization-level Oz + +# Show help +./scripts/compress_wasm.sh --help +``` + +### Optimization Levels + +Available optimization levels for `wasm-opt`: + +- `O0`: No optimization (fastest compilation) +- `O1`: Basic optimization +- `O2`: More optimization +- `O3`: Aggressive optimization +- `Os`: Optimize for size +- `Oz`: Optimize for size aggressively (recommended for deployment) + +### Optimization Flags Used + +The compression applies these optimization flags: + +- `-Oz`: Aggressive size optimization +- `--vacuum`: Remove redundant items +- `--dae`: Dead code elimination +- `--remove-unused-names`: Remove unused names +- `--remove-unused-types`: Remove unused types +- `--merge-blocks`: Merge blocks +- `--simplify-locals`: Simplify local variables +- `--coalesce-locals`: Coalesce local variables + +## File Structure + +``` +contracts/substream_contracts/ +├── target/ +│ ├── wasm32v1-none/release/ +│ │ └── substream_contracts.wasm # Original WASM +│ └── compressed/ +│ └── substream_contracts.optimized.wasm # Compressed WASM +├── Makefile # Build automation +└── src/ + └── lib.rs # Contract source +``` + +## CI/CD Integration + +The GitHub Actions workflow (`.github/workflows/test.yml`) includes: + +1. **Binaryen Installation**: Installs `wasm-opt` and related tools +2. **Contract Building**: Builds the contract using Stellar CLI +3. **WASM Compression**: Runs the compression process +4. **Artifact Upload**: Saves compressed WASM files as workflow artifacts + +### Workflow Steps + +```yaml +- name: Install Binaryen for WASM optimization + run: | + sudo apt-get update + sudo apt-get install -y binaryen + +- name: Build and Compress WASM + run: | + cd contracts/substream_contracts + make build-compressed + +- name: Upload Compressed WASM files + uses: actions/upload-artifact@v3 + with: + name: compressed-wasm + path: contracts/substream_contracts/target/compressed/ +``` + +## Performance Impact + +### Typical Compression Results + +Based on similar Stellar contracts, you can expect: + +- **Size Reduction**: 10-30% smaller WASM files +- **Deployment Cost**: Proportional reduction in deployment fees +- **Runtime Performance**: Minimal to no impact on execution speed +- **Gas Costs**: No increase in transaction gas costs + +### Example Output + +``` +🚀 SubStream Protocol WASM Compression Script +============================================= +🔨 Building contract... +📦 Compressing WASM files with optimization level: Oz + Optimizing substream_contracts.wasm... + ✅ Original: 45678 bytes, Compressed: 34234 bytes, Reduction: 25% +🎉 Compression complete! +📊 Summary: + Total original size: 45678 bytes + Total compressed size: 34234 bytes + Total reduction: 25% + Compressed files saved to: target/compressed +✨ Done! Your optimized WASM files are ready for deployment. +``` + +## Best Practices + +### Development Workflow + +1. **Regular Builds**: Use `make build` during development +2. **Pre-deployment**: Always use `make build-compressed` before deployment +3. **Size Monitoring**: Track compression ratios over time +4. **Testing**: Deploy compressed WASM to testnet first + +### Optimization Tips + +1. **Code Review**: Smaller source code often results in smaller WASM +2. **Dependencies**: Minimize external dependencies +3. **Feature Flags**: Use conditional compilation for unused features +4. **Profile**: Use `cargo bloat` to identify large dependencies + +## Troubleshooting + +### Common Issues + +#### wasm-opt not found +```bash +# Install Binaryen +brew install binaryen # macOS +sudo apt-get install binaryen # Ubuntu +``` + +#### Build fails +```bash +# Check Rust targets +rustup target add wasm32v1-none +rustup target add wasm32-unknown-unknown +``` + +#### Permission denied +```bash +# Make script executable +chmod +x scripts/compress_wasm.sh +``` + +### Debug Mode + +For debugging, use lower optimization levels: + +```bash +./scripts/compress_wasm.sh --optimization-level O1 +``` + +## Integration with Deployment + +### Manual Deployment + +```bash +# Build compressed WASM +make build-compressed + +# Deploy using compressed file +stellar contract deploy \ + --wasm-file target/compressed/substream_contracts.optimized.wasm \ + --source-account your_account \ + --network testnet +``` + +### Automated Deployment + +The compressed WASM files can be automatically deployed using the workflow artifacts: + +1. Download `compressed-wasm` artifact from GitHub Actions +2. Extract the optimized WASM files +3. Deploy using your preferred deployment tool + +## Contributing + +When contributing to the compression system: + +1. Test compression ratios with your changes +2. Verify that compressed contracts still function correctly +3. Update documentation for any new optimization flags +4. Consider the impact on deployment costs + +## License + +This WASM compression implementation is part of the SubStream Protocol Contracts project and follows the same license terms. diff --git a/contracts/substream_contracts/Makefile b/contracts/substream_contracts/Makefile index b971934..c8c865f 100644 --- a/contracts/substream_contracts/Makefile +++ b/contracts/substream_contracts/Makefile @@ -9,8 +9,27 @@ build: stellar contract build @ls -l target/wasm32v1-none/release/*.wasm +build-compressed: build + @echo "Compressing WASM files..." + @mkdir -p target/compressed + @for wasm_file in target/wasm32v1-none/release/*.wasm; do \ + if [ -f "$$wasm_file" ]; then \ + basename=$$(basename "$$wasm_file" .wasm); \ + echo "Optimizing $$basename.wasm..."; \ + wasm-opt -Oz --vacuum --dae --remove-unused-names --remove-unused-types --merge-blocks "$$wasm_file" -o "target/compressed/$$basename.optimized.wasm"; \ + original_size=$$(wc -c < "$$wasm_file"); \ + compressed_size=$$(wc -c < "target/compressed/$$basename.optimized.wasm"); \ + reduction=$$((($$original_size - $$compressed_size) * 100 / $$original_size)); \ + echo "Original: $$original_size bytes, Compressed: $$compressed_size bytes, Reduction: $$reduction%"; \ + fi; \ + done + @ls -l target/compressed/*.optimized.wasm + fmt: cargo fmt --all clean: cargo clean + rm -rf target/compressed + +.PHONY: build-compressed diff --git a/scripts/compress_wasm.sh b/scripts/compress_wasm.sh new file mode 100755 index 0000000..a742db3 --- /dev/null +++ b/scripts/compress_wasm.sh @@ -0,0 +1,141 @@ +#!/bin/bash + +# WASM Compression Script for SubStream Protocol Contracts +# This script uses wasm-opt from Binaryen to optimize WASM binaries for deployment + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Default values +CONTRACT_DIR="contracts/substream_contracts" +OUTPUT_DIR="target/compressed" +OPTIMIZATION_LEVEL="Oz" # Most aggressive optimization + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --contract-dir) + CONTRACT_DIR="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --optimization-level) + OPTIMIZATION_LEVEL="$2" + shift 2 + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "Options:" + echo " --contract-dir DIR Contract directory (default: contracts/substream_contracts)" + echo " --output-dir DIR Output directory for compressed WASM (default: target/compressed)" + echo " --optimization-level L Optimization level (default: Oz)" + echo " Available levels: O0, O1, O2, O3, Os, Oz" + echo " --help, -h Show this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +echo -e "${BLUE}🚀 SubStream Protocol WASM Compression Script${NC}" +echo -e "${BLUE}=============================================${NC}" + +# Check if wasm-opt is available +if ! command -v wasm-opt &> /dev/null; then + echo -e "${RED}❌ Error: wasm-opt not found${NC}" + echo -e "${YELLOW}Please install Binaryen: brew install binaryen${NC}" + exit 1 +fi + +# Check if contract directory exists +if [ ! -d "$CONTRACT_DIR" ]; then + echo -e "${RED}❌ Error: Contract directory '$CONTRACT_DIR' not found${NC}" + exit 1 +fi + +# Change to contract directory +cd "$CONTRACT_DIR" + +# Build the contract first +echo -e "${YELLOW}🔨 Building contract...${NC}" +if ! stellar contract build; then + echo -e "${RED}❌ Build failed${NC}" + exit 1 +fi + +# Create output directory +mkdir -p "$OUTPUT_DIR" + +# Find WASM files +WASM_DIR="target/wasm32v1-none/release" +if [ ! -d "$WASM_DIR" ]; then + echo -e "${RED}❌ Error: WASM directory '$WASM_DIR' not found${NC}" + echo -e "${YELLOW}Make sure the contract was built successfully${NC}" + exit 1 +fi + +WASM_FILES=("$WASM_DIR"/*.wasm) +if [ ! -f "${WASM_FILES[0]}" ]; then + echo -e "${RED}❌ Error: No WASM files found in '$WASM_DIR'${NC}" + exit 1 +fi + +echo -e "${YELLOW}📦 Compressing WASM files with optimization level: $OPTIMIZATION_LEVEL${NC}" + +total_original=0 +total_compressed=0 + +# Process each WASM file +for wasm_file in "${WASM_FILES[@]}"; do + if [ -f "$wasm_file" ]; then + basename=$(basename "$wasm_file" .wasm) + output_file="$OUTPUT_DIR/${basename}.optimized.wasm" + + echo -e "${BLUE} Optimizing $basename.wasm...${NC}" + + # Run wasm-opt with aggressive optimization + wasm_opt_flags="-$OPTIMIZATION_LEVEL --vacuum --dae --remove-unused-names --remove-unused-types --merge-blocks --simplify-locals --coalesce-locals" + + if wasm-opt $wasm_opt_flags "$wasm_file" -o "$output_file"; then + original_size=$(wc -c < "$wasm_file") + compressed_size=$(wc -c < "$output_file") + reduction=$((($original_size - $compressed_size) * 100 / $original_size)) + + total_original=$((total_original + original_size)) + total_compressed=$((total_compressed + compressed_size)) + + echo -e "${GREEN} ✅ Original: $original_size bytes, Compressed: $compressed_size bytes, Reduction: ${reduction}%${NC}" + else + echo -e "${RED} ❌ Failed to optimize $basename.wasm${NC}" + exit 1 + fi + fi +done + +# Calculate total reduction +total_reduction=$((($total_original - $total_compressed) * 100 / $total_original)) + +echo -e "${GREEN}🎉 Compression complete!${NC}" +echo -e "${GREEN}📊 Summary:${NC}" +echo -e "${GREEN} Total original size: $total_original bytes${NC}" +echo -e "${GREEN} Total compressed size: $total_compressed bytes${NC}" +echo -e "${GREEN} Total reduction: ${total_reduction}%${NC}" +echo -e "${GREEN} Compressed files saved to: $OUTPUT_DIR${NC}" + +# List compressed files +echo -e "${BLUE}📁 Compressed files:${NC}" +ls -lh "$OUTPUT_DIR"/*.optimized.wasm 2>/dev/null || echo -e "${YELLOW}No compressed files found${NC}" + +echo -e "${GREEN}✨ Done! Your optimized WASM files are ready for deployment.${NC}"