Skip to content

Instantly share code, notes, and snippets.

@blahah
Last active June 3, 2025 14:44
Show Gist options
  • Save blahah/bfe02e2538c9558710913ab5a392fe09 to your computer and use it in GitHub Desktop.
Save blahah/bfe02e2538c9558710913ab5a392fe09 to your computer and use it in GitHub Desktop.
RPC/function delegation with hypercore
const Hypercore = require('hypercore')
const Hyperswarm = require('hyperswarm')
const crypto = require('hypercore-crypto')
// Available functions for delegation
const AVAILABLE_FUNCTIONS = {
add: (a, b) => a + b,
multiply: (a, b) => a * b,
greet: (name) => `Hello, ${name}!`,
getCurrentTime: () => new Date().toISOString(),
processData: (data) => ({
processed: true,
length: data.length,
uppercase: data.toUpperCase()
}),
fibonacci: (n) => {
if (n <= 1) return n
return AVAILABLE_FUNCTIONS.fibonacci(n - 1) + AVAILABLE_FUNCTIONS.fibonacci(n - 2)
}
}
class DemoRunner {
constructor() {
this.cleanup = []
}
log(message, type = 'info') {
const timestamp = new Date().toISOString().substr(11, 8)
const prefix = {
info: 'πŸ“',
success: 'βœ…',
error: '❌',
delegator: 'πŸ“€',
executor: '⚑',
peer1: 'πŸ”΅',
peer2: '🟣'
}[type] || 'πŸ“'
console.log(`[${timestamp}] ${prefix} ${message}`)
}
async sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async runOneWayDemo() {
this.log('='.repeat(60), 'info')
this.log('DEMO 1: ONE-WAY FUNCTION DELEGATION', 'info')
this.log('='.repeat(60), 'info')
// Create delegator (sends function calls)
const delegatorFeed = new Hypercore('./demo-oneway-feed', {
valueEncoding: 'json'
})
await delegatorFeed.ready()
const delegatorSwarm = new Hyperswarm()
this.cleanup.push(() => delegatorSwarm.destroy())
this.cleanup.push(() => delegatorFeed.close())
// Create executor (receives and runs function calls)
const executorFeed = new Hypercore('./demo-oneway-feed', delegatorFeed.key, {
valueEncoding: 'json'
})
await executorFeed.ready()
const executorSwarm = new Hyperswarm()
this.cleanup.push(() => executorSwarm.destroy())
this.cleanup.push(() => executorFeed.close())
this.log(`Feed key: ${delegatorFeed.key.toString('hex')}`, 'info')
// Set up executor to listen for function calls
let executedCount = 0
const executeFunction = async (entry) => {
const { id, function: funcName, args, timestamp } = entry
this.log(`Received: ${funcName}(${args.join(', ')}) [ID: ${id.substr(0, 8)}...]`, 'executor')
try {
if (AVAILABLE_FUNCTIONS[funcName]) {
const result = AVAILABLE_FUNCTIONS[funcName](...args)
this.log(`Executed -> ${JSON.stringify(result)}`, 'success')
executedCount++
} else {
this.log(`Unknown function: ${funcName}`, 'error')
}
} catch (error) {
this.log(`Execution error: ${error.message}`, 'error')
}
}
// Process existing entries
for (let i = 0; i < executorFeed.length; i++) {
const entry = await executorFeed.get(i)
await executeFunction(entry)
}
// Listen for new entries
executorFeed.on('append', async () => {
const entry = await executorFeed.get(executorFeed.length - 1)
await executeFunction(entry)
})
// Set up networking
delegatorSwarm.join(delegatorFeed.discoveryKey)
executorSwarm.join(executorFeed.discoveryKey)
let connected = false
delegatorSwarm.on('connection', (connection) => {
if (!connected) {
this.log('Delegator connected to executor', 'success')
connected = true
}
delegatorFeed.replicate(connection)
})
executorSwarm.on('connection', (connection) => {
executorFeed.replicate(connection)
})
// Wait for connection
await this.sleep(2000)
// Send function calls from delegator
const calls = [
{ func: 'add', args: ['5', '3'] },
{ func: 'multiply', args: ['4', '7'] },
{ func: 'greet', args: ['Alice'] },
{ func: 'getCurrentTime', args: [] },
{ func: 'processData', args: ['hello-world'] },
{ func: 'fibonacci', args: ['7'] }
]
this.log('Starting function delegation...', 'delegator')
for (const call of calls) {
const functionCall = {
id: crypto.randomBytes(16).toString('hex'),
function: call.func,
args: call.args,
timestamp: Date.now()
}
this.log(`Delegating: ${call.func}(${call.args.join(', ')})`, 'delegator')
await delegatorFeed.append(functionCall)
await this.sleep(1000)
}
// Wait for all executions to complete
await this.sleep(2000)
this.log(`One-way demo completed! Executed ${executedCount} functions`, 'success')
// Cleanup
await delegatorSwarm.destroy()
await executorSwarm.destroy()
await delegatorFeed.close()
await executorFeed.close()
}
async runBidirectionalDemo() {
this.log('='.repeat(60), 'info')
this.log('DEMO 2: BIDIRECTIONAL FUNCTION DELEGATION', 'info')
this.log('='.repeat(60), 'info')
// Create feeds for requests and responses
const requestsFeed = new Hypercore('./demo-requests', {
valueEncoding: 'json'
})
const responsesFeed = new Hypercore('./demo-responses', {
valueEncoding: 'json'
})
await requestsFeed.ready()
await responsesFeed.ready()
this.log(`Requests feed: ${requestsFeed.key.toString('hex')}`, 'info')
this.log(`Responses feed: ${responsesFeed.key.toString('hex')}`, 'info')
// Create two peers
const peer1 = await this.createBidirectionalPeer('Peer1', requestsFeed, responsesFeed)
const peer2 = await this.createBidirectionalPeer('Peer2', requestsFeed, responsesFeed)
// Wait for connections
await this.sleep(2000)
// Demo sequence: Peer1 calls functions on Peer2, then vice versa
this.log('Starting bidirectional function calls...', 'info')
const demo1Calls = [
{ peer: peer1, func: 'add', args: ['10', '5'] },
{ peer: peer1, func: 'greet', args: ['Bob'] },
{ peer: peer1, func: 'fibonacci', args: ['6'] }
]
for (const call of demo1Calls) {
try {
this.log(`${call.peer.name} calling: ${call.func}(${call.args.join(', ')})`, 'peer1')
const result = await call.peer.callRemoteFunction(call.func, ...call.args)
this.log(`${call.peer.name} received result: ${JSON.stringify(result)}`, 'success')
} catch (error) {
this.log(`${call.peer.name} call failed: ${error.message}`, 'error')
}
await this.sleep(1500)
}
await this.sleep(1000)
const demo2Calls = [
{ peer: peer2, func: 'multiply', args: ['3', '8'] },
{ peer: peer2, func: 'processData', args: ['test-data'] },
{ peer: peer2, func: 'getCurrentTime', args: [] }
]
for (const call of demo2Calls) {
try {
this.log(`${call.peer.name} calling: ${call.func}(${call.args.join(', ')})`, 'peer2')
const result = await call.peer.callRemoteFunction(call.func, ...call.args)
this.log(`${call.peer.name} received result: ${JSON.stringify(result)}`, 'success')
} catch (error) {
this.log(`${call.peer.name} call failed: ${error.message}`, 'error')
}
await this.sleep(1500)
}
this.log('Bidirectional demo completed!', 'success')
// Cleanup
await peer1.cleanup()
await peer2.cleanup()
await requestsFeed.close()
await responsesFeed.close()
}
async createBidirectionalPeer(name, requestsFeed, responsesFeed) {
const swarm = new Hyperswarm()
const pendingRequests = new Map()
const processedRequests = new Set()
// Join swarms
swarm.join(requestsFeed.discoveryKey)
swarm.join(responsesFeed.discoveryKey)
swarm.on('connection', (connection) => {
requestsFeed.replicate(connection)
responsesFeed.replicate(connection)
})
// Process requests
const processRequest = async (request) => {
const { id, function: funcName, args, from } = request
if (from === name || processedRequests.has(id)) {
return
}
processedRequests.add(id)
this.log(`${name} executing: ${funcName}(${args.join(', ')}) for ${from}`, name.toLowerCase())
try {
if (AVAILABLE_FUNCTIONS[funcName]) {
const result = AVAILABLE_FUNCTIONS[funcName](...args)
const response = {
id: crypto.randomBytes(16).toString('hex'),
requestId: id,
result,
from: name,
to: from,
timestamp: Date.now(),
success: true
}
await responsesFeed.append(response)
}
} catch (error) {
const response = {
id: crypto.randomBytes(16).toString('hex'),
requestId: id,
error: error.message,
from: name,
to: from,
timestamp: Date.now(),
success: false
}
await responsesFeed.append(response)
}
}
// Process responses
const processResponse = (response) => {
const { requestId, result, error, to, success } = response
if (to !== name) return
if (pendingRequests.has(requestId)) {
const { resolve, reject } = pendingRequests.get(requestId)
if (success) {
resolve(result)
} else {
reject(new Error(error))
}
pendingRequests.delete(requestId)
}
}
// Set up listeners
requestsFeed.on('append', async () => {
const request = await requestsFeed.get(requestsFeed.length - 1)
await processRequest(request)
})
responsesFeed.on('append', async () => {
const response = await responsesFeed.get(responsesFeed.length - 1)
processResponse(response)
})
// Process existing entries
for (let i = 0; i < requestsFeed.length; i++) {
const request = await requestsFeed.get(i)
await processRequest(request)
}
for (let i = 0; i < responsesFeed.length; i++) {
const response = await responsesFeed.get(i)
processResponse(response)
}
return {
name,
swarm,
async callRemoteFunction(functionName, ...args) {
return new Promise(async (resolve, reject) => {
const requestId = crypto.randomBytes(16).toString('hex')
const request = {
id: requestId,
function: functionName,
args,
from: name,
timestamp: Date.now()
}
pendingRequests.set(requestId, { resolve, reject })
setTimeout(() => {
if (pendingRequests.has(requestId)) {
pendingRequests.delete(requestId)
reject(new Error('Request timeout'))
}
}, 5000)
await requestsFeed.append(request)
})
},
async cleanup() {
await swarm.destroy()
}
}
}
async run() {
console.log('πŸš€ Function Delegation Demo')
console.log('===========================')
console.log('This demo shows how to delegate function calls between peers')
console.log('using hypercore feeds for communication.\n')
try {
await this.runOneWayDemo()
await this.sleep(2000)
await this.runBidirectionalDemo()
this.log('πŸŽ‰ All demos completed successfully!', 'success')
this.log('Check the examples in this directory to see the full implementations.', 'info')
} catch (error) {
this.log(`Demo failed: ${error.message}`, 'error')
console.error(error)
} finally {
// Cleanup any remaining resources
for (const cleanup of this.cleanup) {
try {
await cleanup()
} catch (e) {
// Ignore cleanup errors
}
}
process.exit(0)
}
}
}
// Run the demo
const demo = new DemoRunner()
demo.run().catch(console.error)
➜ function-delegation node hypercore-function-delegation.js
🎯 Simple Function Delegation Demo
==================================
πŸ“‘ Created hypercore feed
Feed key: ac719971869dcd904f9309bb9717689e38b416c09f9c5e680300d0cbbfa29777
⚑ Setting up executor...
πŸ“€ Starting delegation...
πŸ“€ Delegating: add(5, 3)
πŸ“₯ Received call: add(5, 3)
βœ… Result: 8
πŸ“€ Delegating: greet(Alice)
πŸ“₯ Received call: greet(Alice)
βœ… Result: Hello, Alice!
πŸ“€ Delegating: reverse(hello)
πŸ“₯ Received call: reverse(hello)
βœ… Result: olleh
πŸ“€ Delegating: add(10, 20)
πŸ“₯ Received call: add(10, 20)
βœ… Result: 30
πŸ“€ Delegating: unknown(test)
πŸ“₯ Received call: unknown(test)
❌ Unknown function: unknown
πŸŽ‰ Demo completed!
This shows how one peer can send function calls
that another peer receives and executes in real-time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment