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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## [Unreleased]

### Added
- **PeTTa Integration**: MeTTa smart contract execution via SWI-Prolog
- New system contract URN `rho:petta:execute` for executing MeTTa code from Rholang
- Core execution function `petta_execute()` with 10-second timeout protection
- Example contracts in `rholang/examples/system-contract/swipl/` demonstrating pattern matching and recursion
- Full documentation:
- URN specification: `rholang/docs/PETTA_URN_SPECIFICATION.md`
- Test documentation: `rholang/tests/PETTA_TESTS_SUMMARY.md` and `TIMEOUT_TESTS.md`
- Example README: `rholang/examples/system-contract/swipl/README.md`
- API documentation via doc comments on all public items

## [v0.1.0-SNAPSHOT] - 2023-05-15
- Added new features
Expand Down
8 changes: 8 additions & 0 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ __Note__ Successfully building from source requires attending to all of the prer
### Prerequisites

* [Environment set up](README.md#installation).
* **SWI-Prolog** - Required runtime dependency for MeTTa smart contract execution via PeTTa submodule
- Nix/direnv users: Already provisioned in the development shell
- Manual installation:
- macOS: `brew install swi-prolog`
- Ubuntu/Debian: `apt-get install swi-prolog`
- Fedora: `dnf install pl`
- The `swipl` binary must be in your PATH
- PeTTa submodule must be initialized: `git submodule update --init --recursive`

<!-- Setup using _nix_ can be found in the [nix](./nix) directory.

Expand Down
9 changes: 9 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@
package = just;
help = "Just command runner for local development tasks";
}
{
name = "swipl";
package = swi-prolog;
help = "SWI-Prolog interpreter (required for PeTTa/MeTTa execution)";
}
];
packages = [
# Required for Python packages with native extensions (grpcio, etc.)
Expand Down Expand Up @@ -188,6 +193,10 @@
name = "BLOOP_JAVA_OPTS";
value = "-Xmx4G -XX:+UseZGC -Xss4m -Xms1g";
}
{
name = "PETTA_PATH";
value = "./PeTTa";
}
];
};
}
Expand Down
11 changes: 11 additions & 0 deletions node/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ COPY comm/ ./comm/
COPY rholang/ ./rholang/
COPY node/ ./node/

# Copy PeTTa submodule (required for MeTTa contract execution)
COPY PeTTa/ ./PeTTa/

# Configure pkg-config for cross-compilation to find target OpenSSL libraries
# xx-apt installs target libs in the xx sysroot, we need to tell pkg-config where they are
# Enable AES CPU features for gxhash dependency (required by PathMap crate).
Expand Down Expand Up @@ -88,12 +91,14 @@ FROM debian:bookworm-slim AS runtime-base

# Install runtime dependencies
# Note: grpcurl and jq for healthcheck (matching build.sbt healthcheck)
# Note: swi-prolog is required for PeTTa/MeTTa contract execution
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
libc6 \
libjemalloc2 \
curl \
swi-prolog \
&& rm -rf /var/lib/apt/lists/*

# Install grpcurl and jq for healthcheck (matching build.sbt)
Expand Down Expand Up @@ -149,6 +154,9 @@ COPY --from=builder /build/casper/src/main/resources ./casper/src/main/resources
# Copy rholang examples (matching build.sbt: directory((baseDirectory in rholang).value / "examples"))
COPY --from=builder /build/rholang/examples ./examples

# Copy PeTTa submodule (required for MeTTa contract execution at runtime)
COPY --from=builder /build/PeTTa ./PeTTa

# Create entrypoint script for Rust (adapted from docker-entrypoint.sh)
# Copy the Rust-specific entrypoint script from node directory
# Note: This Dockerfile should be built from workspace root
Expand Down Expand Up @@ -176,6 +184,9 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
LABEL maintainer="F1r3fly.io LCA https://f1r3fly.io/"
LABEL version="0.1.0"

# Set PETTA_PATH environment variable for MeTTa contract execution
ENV PETTA_PATH=/opt/docker/PeTTa

# Switch to daemon user (matching build.sbt: daemonUser in Docker := "daemon")
USER daemon

Expand Down
6 changes: 0 additions & 6 deletions rholang/examples/system-contract/swipl/01-compile-metta.rho

This file was deleted.

5 changes: 5 additions & 0 deletions rholang/examples/system-contract/swipl/01-swap.metta
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(= (swap (Pair $x $y)) (Pair $y $x))

!(swap (Pair 1 2))

!(swap (Pair x y))
6 changes: 6 additions & 0 deletions rholang/examples/system-contract/swipl/01-swap.rho
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
new executePetta(`rho:petta:execute`), stdout(`rho:io:stdout`), retCh in {
executePetta!("(= (swap (Pair $x $y)) (Pair $y $x)) !(swap (Pair 1 3)) !(swap (Pair x y))", *retCh) |
for(@ok <- retCh) {
stdout!(ok)
}
}
13 changes: 13 additions & 0 deletions rholang/examples/system-contract/swipl/02-fib-long.metta
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(= (fib-tr $n $a $b)
(if (== $n 0)
$a
(fib-tr (- $n 1) $b (+ $a $b))))

(= (fib $n)
(fib-tr $n 0 1))

(= (main $_) (fib 1000000))

!(main dummy)


6 changes: 6 additions & 0 deletions rholang/examples/system-contract/swipl/02-fib-long.rho
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
new executePetta(`rho:petta:execute`), stdout(`rho:io:stdout`), retCh in {
executePetta!("(= (fib-tr $n $a $b) (if (== $n 0) $a (fib-tr (- $n 1) $b (+ $a $b)))) (= (fib $n) (fib-tr $n 0 1)) (= (main $x) (fib 1000000)) !(main dummy)", *retCh) |
for(@ok <- retCh) {
stdout!(ok)
}
}
143 changes: 143 additions & 0 deletions rholang/examples/system-contract/swipl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# PeTTa (SWI-Prolog + MeTTa) Examples

This directory contains Rholang examples demonstrating the `rho:petta:execute`
system contract, which provides access to the MeTTA language.

## What is PeTTa?

**PeTTa** is an interpreter for MeTTa written in SWI-Prolog.

This integration allows Rholang contracts to perform advanced symbolic reasoning,
pattern matching, and AI-style computations.

## Prerequisites

See the prerequisites section in the DEVELOPER.md file located in the project's
folder.

## MeTTa entry point

Standard MeTTa programs do not require an entry point. Any definitions and queries
are added and executed (respectively) in the order of the program.

However, programs provided to the `rho::petta::execute` contract MUST contain
an entry point called `main`. This entry point MUST be defined as a function
with a single parameter (which serves no purpose and should be ignored).

## Examples Overview

### 01-swap.rho - Pattern Matching Basics

Demonstrates basic MeTTa pattern matching by defining a `swap` function that reverses a pair.

### 02-fib-long.rho - Recursive Computation

Computes a large Fibonacci number (fib(1000000)) using tail recursion,
which on consumer hardware should exceed the timeout of 10 seconds currently
defined for all MeTTa computations.


**MeTTa code:**
```metta
(= (fib-tr $n $a $b) (if (== $n 0) $a (fib-tr (- $n 1) $b (+ $a $b))))
(= (fib $n) (fib-tr $n 0 1))
!(fib 1000000)
```

### Return Value Structure

PeTTa always returns results wrapped in a `{"results": [...]}` JSON object, which is converted to a Rholang map:

```rholang
{
"results": [result1, result2, ...] // Rholang list
}
```

## Common Patterns

### Pattern 1: Simple Computation

```rholang
new executePetta(`rho:petta:execute`), retCh in {
executePetta!("!(+ 1 2)", *retCh) |
for(@result <- retCh) {
// result = {"results": [3]}
match result {
{"results": [answer]} => {
stdout!(answer) // Prints: 3
}
}
}
}
```

### Pattern 2: Multiple Calls

```rholang
new executePetta(`rho:petta:execute`), ret1, ret2 in {
executePetta!("!(+ 1 2)", *ret1) |
executePetta!("!(* 3 4)", *ret2) |

for(@r1 <- ret1; @r2 <- ret2) {
stdout!([r1, r2])
}
}
```

## Troubleshooting

### Error: "Can't find PeTTa"

**Cause:** `$PETTA_PATH` points to invalid location

**Solution:**
```bash
# Check PeTTa location
ls ./PeTTa/src/metta.pl

# Or set explicitly
export PETTA_PATH=/full/path/to/PeTTa
```

### Error: "swipl: command not found"

**Cause:** SWI-Prolog not installed or not in PATH

**Solution:**
```bash
# Install SWI-Prolog
brew install swi-prolog # macOS
apt-get install swi-prolog # Ubuntu/Debian

# Verify
which swipl
```

### Timeout Errors

**Cause:** Computation exceeded 10-second limit

**Solutions:**
1. Break computation into smaller steps
2. Use more efficient algorithms
3. Pre-compute complex results off-chain

### No Output

**Cause:** Error occurred (doesn't send on ack channel)

**Solution:** Check node logs for error details:
```bash
tail -f ~/.rnode/rnode.log
```

## Security Notes

⚠️ **Important Security Considerations: this feature is EXPERIMENTAL.**

1. **Untrusted Code:** Never execute untrusted MeTTa code - there is currently
no sandboxing at language level
2. **Timeouts:** All execution limited to 10 seconds to prevent DoS
3. **Non-Deterministic:** Results are cached for replay (consensus safety)
4. **Resource Limits:** System memory limits apply
35 changes: 15 additions & 20 deletions rholang/src/rust/interpreter/rho_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1026,26 +1026,21 @@ fn std_rho_chroma_processes() -> Vec<Definition> {
}

fn std_swipl_processes() -> Vec<Definition> {
vec![
Definition {
urn: "rho:petta:compile".to_string(),
fixed_channel: FixedChannels::swipl_compile_petta(),
arity: 2,
body_ref: BodyRefs::SWIPL_COMPILE_PETTA,
handler: Box::new(|ctx| {
Box::new(move |args| {
let ctx = ctx.clone();
Box::pin(async move {
ctx.system_processes
.clone()
.swipl_compile_petta(args)
.await
})
})
}),
remainder: None,
},
]
vec![Definition {
urn: "rho:petta:execute".to_string(),
fixed_channel: FixedChannels::swipl_execute_petta(),
arity: 2,
body_ref: BodyRefs::SWIPL_EXECUTE_PETTA,
handler: Box::new(|ctx| {
Box::new(move |args| {
let ctx = ctx.clone();
Box::pin(
async move { ctx.system_processes.clone().swipl_execute_petta(args).await },
)
})
}),
remainder: None,
}]
}

fn dispatch_table_creator(
Expand Down
Loading