Skip to content

Commit a744f6e

Browse files
0xrinegadeclaude
andcommitted
feat: Add sBPF compiler lamport ops, BBS federation, and TUI golden tests
## sBPF Compiler (crates/ovsm) - Add `set-lamports` function for SOL transfers between accounts - Add `account-executable` check for program detection - Document instruction-data access limitation (V1 buffer scanning) - Update SBPF_COMPILER_BUILTINS.md with new functions ## BBS Federation (src/utils/bbs) - Reduce sync interval from 60s to 10s for faster propagation - Fix API response parsing (support wrapped format) - Add proper error handling for sync operations - Update http_server with federation endpoints ## TUI Golden Tests (tests/golden/tui) - Add comprehensive golden test suite for TUI components - Cover AI insights (critical/high/medium/low risk) - Test wallet behaviors (bot, exchange, mixer) - Test SOL flow, token volumes, transfer feed states - Add screenshot test infrastructure ## Documentation - Add BBS system documentation (docs/bbs-system.md) - Update CLAUDE.md with advisor guidelines - Update README with new features 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 7765cfd commit a744f6e

Some content is hidden

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

47 files changed

+4951
-251
lines changed

CLAUDE.md

Lines changed: 216 additions & 89 deletions
Large diffs are not rendered by default.

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,3 +791,59 @@ osvm stream --programs pumpfun | grep -i "graduate"
791791
- 🎨 [Interactive Demo](docs/streaming-demo.html) - HTML visualization UI
792792
- 💻 [Program Aliases](src/utils/program_aliases.rs) - Full alias list
793793

794+
## 📡 BBS System - Decentralized Agent-Human Communication
795+
796+
OSVM includes a **Bulletin Board System (BBS)** for decentralized communication between AI agents and humans. It supports multiple transport layers including HTTP, Meshtastic radio for off-grid operation, and **on-chain Solana registry** for trustless peer discovery.
797+
798+
```bash
799+
# Initialize and use BBS
800+
osvm bbs init # Initialize database
801+
osvm bbs boards list # List available boards
802+
osvm bbs post GENERAL "Hello from OSVM!" # Post a message
803+
osvm bbs read GENERAL # Read messages
804+
osvm bbs server --port 8080 # Start HTTP API server
805+
```
806+
807+
### On-Chain Registry (Solana Devnet)
808+
809+
BBS nodes can register on Solana blockchain for **trustless, censorship-resistant peer discovery**:
810+
811+
```bash
812+
# Register your node on-chain
813+
osvm bbs registry register "http://my-public-ip:8080" "MyNodeName"
814+
815+
# List all registered nodes
816+
osvm bbs registry list
817+
818+
# Discover and add peers from registry
819+
osvm bbs registry discover
820+
821+
# Send heartbeat (update last_seen)
822+
osvm bbs registry heartbeat
823+
```
824+
825+
**Program ID:** `CrCWo8atPHMtDiun76czDood6RnPYVvzxPmoMMP4TSCG` (Devnet - **LIVE**)
826+
827+
### Federation & Off-Grid
828+
829+
```bash
830+
# Peer-to-peer federation
831+
osvm bbs peers add http://192.168.1.100:8080
832+
osvm bbs peers sync
833+
834+
# Meshtastic radio (off-grid)
835+
osvm bbs radio connect /dev/ttyUSB0
836+
osvm bbs radio send "Message via radio"
837+
```
838+
839+
**Features:**
840+
-**On-Chain Registry**: Trustless peer discovery via Solana devnet
841+
-**Federation**: Peer-to-peer message sync with deduplication
842+
-**Meshtastic Radio**: Off-grid LoRa mesh communication
843+
-**Agent Support**: AI agents can register and post as verified identities
844+
-**HTTP API**: REST endpoints + WebSocket for real-time updates
845+
846+
**Documentation:**
847+
- 📖 [BBS System Guide](docs/bbs-system.md) - Complete documentation
848+
- 🔗 [On-Chain Registry](docs/bbs-system.md#on-chain-registry-solana-devnet) - Solana integration
849+

crates/ovsm/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- `mem-load1` - Load 1 byte from memory pointer + offset
2121
- 🔧 **Logging Syscall**
2222
- `sol_log_pubkey` - Log 32-byte public key in base58 format
23+
- 🔧 **Account Modification**
24+
- `set-lamports` - Set lamport balance for SOL transfers
25+
- 🔧 **Account Flags**
26+
- `account-executable` - Check if account is an executable program
2327
- 📝 **New Documentation**
2428
- `SBPF_COMPILER_BUILTINS.md` - Complete reference for sBPF compiler built-in functions
2529

crates/ovsm/SBPF_COMPILER_BUILTINS.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -755,11 +755,54 @@ The compiler reports estimated CU usage:
755755

756756
---
757757

758+
## New in v1.0.5 (Session 2)
759+
760+
### `set-lamports`
761+
762+
**Signature:** `(set-lamports idx value)`
763+
**Description:** Set the lamport balance of account at index (for SOL transfers)
764+
**Parameters:**
765+
- `idx` - Account index (0-based)
766+
- `value` - New lamport value (u64)
767+
**Returns:** None
768+
**Note:** Account must be writable and owned by your program or System Program
769+
770+
```lisp
771+
;; Transfer SOL: reduce account 0, increase account 1
772+
(define current-0 (account-lamports 0))
773+
(define current-1 (account-lamports 1))
774+
(define transfer-amount 1000000000) ;; 1 SOL
775+
776+
(set-lamports 0 (- current-0 transfer-amount))
777+
(set-lamports 1 (+ current-1 transfer-amount))
778+
```
779+
780+
---
781+
782+
### `account-executable`
783+
784+
**Signature:** `(account-executable idx)`
785+
**Description:** Check if account is an executable program
786+
**Parameters:**
787+
- `idx` - Account index (0-based)
788+
**Returns:** 1 if executable, 0 if not
789+
**Tested:** ✅ Verified on devnet
790+
791+
```lisp
792+
;; Verify account is a program
793+
(if (account-executable 0)
794+
(sol_log_ "Account is executable")
795+
(sol_log_ "Account is not a program"))
796+
```
797+
798+
---
799+
758800
## Future Features (Planned)
759801

802+
- [ ] `instruction-data-len` - Get instruction data length (requires scanning accounts buffer)
803+
- [ ] `instruction-data-ptr` - Get pointer to instruction data
760804
- [ ] Cross-Program Invocation (CPI)
761805
- [ ] Program Derived Addresses (PDAs)
762-
- [ ] Account data modification
763806
- [ ] Token Program integration
764807
- [ ] Custom error codes
765808

crates/ovsm/src/compiler/ir.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,91 @@ impl IrGenerator {
593593
return Ok(Some(dst));
594594
}
595595

596+
// Handle (set-lamports idx value) - set lamport balance for account
597+
// This is how SOL transfers work - directly modify lamports
598+
// IMPORTANT: Account must be writable and owned by your program (or system program)
599+
if name == "set-lamports" && args.len() == 2 {
600+
let idx_reg = self.generate_expr(&args[0].value)?
601+
.ok_or_else(|| Error::runtime("set-lamports index has no result"))?;
602+
let value_reg = self.generate_expr(&args[1].value)?
603+
.ok_or_else(|| Error::runtime("set-lamports value has no result"))?;
604+
605+
let accounts_ptr = *self.var_map.get("accounts")
606+
.ok_or_else(|| Error::runtime("accounts not available"))?;
607+
608+
let eight_reg = self.alloc_reg();
609+
self.emit(IrInstruction::ConstI64(eight_reg, 8));
610+
611+
let account_size = self.alloc_reg();
612+
self.emit(IrInstruction::ConstI64(account_size, 10344));
613+
614+
let account_offset = self.alloc_reg();
615+
self.emit(IrInstruction::Mul(account_offset, idx_reg, account_size));
616+
617+
let base_offset = self.alloc_reg();
618+
self.emit(IrInstruction::Add(base_offset, eight_reg, account_offset));
619+
620+
// lamports offset = 72
621+
let lamports_offset = self.alloc_reg();
622+
self.emit(IrInstruction::ConstI64(lamports_offset, 72));
623+
624+
let total_offset = self.alloc_reg();
625+
self.emit(IrInstruction::Add(total_offset, base_offset, lamports_offset));
626+
627+
let addr = self.alloc_reg();
628+
self.emit(IrInstruction::Add(addr, accounts_ptr, total_offset));
629+
630+
// Store the new lamports value
631+
self.emit(IrInstruction::Store(addr, value_reg, 0));
632+
return Ok(None); // Store has no result
633+
}
634+
635+
// Handle (account-executable idx) - check if account is executable (1 byte at offset 3)
636+
if name == "account-executable" && args.len() == 1 {
637+
let idx_reg = self.generate_expr(&args[0].value)?
638+
.ok_or_else(|| Error::runtime("account-executable index has no result"))?;
639+
640+
let accounts_ptr = *self.var_map.get("accounts")
641+
.ok_or_else(|| Error::runtime("accounts not available"))?;
642+
643+
let eight_reg = self.alloc_reg();
644+
self.emit(IrInstruction::ConstI64(eight_reg, 8));
645+
646+
let account_size = self.alloc_reg();
647+
self.emit(IrInstruction::ConstI64(account_size, 10344));
648+
649+
let account_offset = self.alloc_reg();
650+
self.emit(IrInstruction::Mul(account_offset, idx_reg, account_size));
651+
652+
let base_offset = self.alloc_reg();
653+
self.emit(IrInstruction::Add(base_offset, eight_reg, account_offset));
654+
655+
// executable is at offset 3 from account start
656+
let exec_offset = self.alloc_reg();
657+
self.emit(IrInstruction::ConstI64(exec_offset, 3));
658+
659+
let total_offset = self.alloc_reg();
660+
self.emit(IrInstruction::Add(total_offset, base_offset, exec_offset));
661+
662+
let addr = self.alloc_reg();
663+
self.emit(IrInstruction::Add(addr, accounts_ptr, total_offset));
664+
665+
let raw = self.alloc_reg();
666+
self.emit(IrInstruction::Load(raw, addr, 0));
667+
668+
let mask = self.alloc_reg();
669+
self.emit(IrInstruction::ConstI64(mask, 0xFF));
670+
671+
let dst = self.alloc_reg();
672+
self.emit(IrInstruction::And(dst, raw, mask));
673+
return Ok(Some(dst));
674+
}
675+
676+
// NOTE: instruction-data-len and instruction-data-ptr are NOT YET IMPLEMENTED
677+
// In Solana sBPF V1, instruction data is at the END of the accounts buffer,
678+
// requiring scanning through variable-length account entries to find it.
679+
// TODO: Implement proper instruction data access by iterating through accounts.
680+
596681
// Handle (account-data-ptr idx) - get pointer to account data
597682
// Data starts at offset 88 from account start (after data_len at 80)
598683
// For account 0: 8 + 88 = 96 from input start

0 commit comments

Comments
 (0)