Skip to content

Instantly share code, notes, and snippets.

@blahah
Last active June 4, 2025 09:47
Show Gist options
  • Save blahah/44f0d06bc2cbc53d89f195f2febe4ab2 to your computer and use it in GitHub Desktop.
Save blahah/44f0d06bc2cbc53d89f195f2febe4ab2 to your computer and use it in GitHub Desktop.
Distributed n-n RPC with request-fulfilment pattern using Autobase/blind-pairing/hypercore ecosystem
const BlindPairing = require('blind-pairing')
const Autobase = require('autobase')
const Corestore = require('corestore')
const Hyperswarm = require('hyperswarm')
const crypto = require('hypercore-crypto')
const z32 = require('z32')
const FUNCTIONS = {
add: (a, b) => Number(a) + Number(b),
multiply: (a, b) => Number(a) * Number(b),
greet: (name) => `Hello ${name} from ${process.pid}!`,
fibonacci: (n) => {
const num = Number(n)
if (num <= 1) return num
return FUNCTIONS.fibonacci(num - 1) + FUNCTIONS.fibonacci(num - 2)
},
processData: (data) => ({
processed: true,
length: data.length,
uppercase: data.toUpperCase(),
processedBy: process.pid
})
}
class BlindPairingRPCPeer {
constructor(name, isCreator = false) {
this.name = name
this.isCreator = isCreator
this.store = null
this.autobase = null
this.swarm = new Hyperswarm()
this.pairing = new BlindPairing(this.swarm)
this.member = null
this.pendingRequests = new Map()
this.processedMessages = new Set()
this.stats = { sent: 0, fulfilled: 0, received: 0 }
this.invite = null
}
log(message, type = 'info') {
const time = new Date().toISOString().substr(11, 8)
const colors = { alice: 'πŸ”΅', bob: '🟒', charlie: '🟣' }
const icon = colors[this.name.toLowerCase()] || 'πŸ“'
console.log(`[${time}] ${icon} ${this.name}: ${message}`)
}
async start(invite = null) {
this.store = new Corestore(`./blind-rpc-${this.name.toLowerCase()}`)
// Setup networking FIRST
const store = this.store
this.swarm.on('connection', async (connection) => {
this.log('Connected to peer, setting up replication')
await store.replicate(connection)
})
if (this.isCreator) {
await this.startAsCreator()
} else {
await this.joinWithInvite(invite)
}
// Listen for updates
this.autobase.on('update', () => {
this.processMessages()
})
await this.processMessages()
}
async startAsCreator() {
this.log('Starting as creator...')
// Create autobase
this.autobase = new Autobase(this.store, null, {
apply: this.apply.bind(this),
open: this.open.bind(this),
valueEncoding: 'json'
})
await this.autobase.ready()
this.log(`Created autobase: ${this.autobase.key.toString('hex').substr(0, 16)}...`)
// Get our local writer key and add ourselves as the first writer
const localCore = this.store.get({ name: 'local' })
await localCore.ready()
const localKey = localCore.key
// Add ourselves as the first writer
await this.autobase.append({
type: 'addWriter',
key: localKey.toString('hex'),
addedBy: this.name,
timestamp: Date.now()
})
await this.autobase.update()
this.log(`Added self as first writer - inputs: ${this.autobase.inputs?.length || 0}`)
// Setup pairing member to accept joiners
this.member = this.pairing.addMember({
discoveryKey: this.autobase.discoveryKey,
onadd: (candidate) => this.onAddMember(candidate)
})
await this.member.flushed()
// Create invite
this.invite = await this.createInvite()
this.log(`Invite created: ${this.invite}`)
// Join swarm
const discovery = this.swarm.join(this.autobase.discoveryKey)
await discovery.flushed()
}
async joinWithInvite(invite) {
if (!invite) {
throw new Error('Invite required for joiners')
}
this.log(`Joining with invite: ${invite}`)
// Get our local writer key to send as userData
const localCore = this.store.get({ name: 'writer' })
await localCore.ready()
const localKey = localCore.key
this.localCore = this.store.get({ name: 'local' })
await this.localCore.ready()
const localKey = this.localCore.key
// Add candidate to join the creator
const candidate = this.pairing.addCandidate({
invite: z32.decode(invite),
userData: localKey,
onadd: (result) => this.onJoinSuccess(result)
})
return new Promise((resolve, reject) => {
this.resolveJoin = resolve
this.rejectJoin = reject
})
}
async onJoinSuccess(result) {
this.log('Join successful, creating autobase...')
// Ensure our local writer core is available in the store before creating autobase
if (!this.localCore) {
this.localCore = this.store.get({ name: 'local' })
await this.localCore.ready()
}
this.autobase = new Autobase(this.store, result.key, {
apply: this.apply.bind(this),
open: this.open.bind(this),
valueEncoding: 'json',
encryptionKey: result.encryptionKey
})
await this.autobase.ready()
// Check if autobase uses our local writer core
this.log(`Local core key: ${this.localCore.key.toString('hex').substr(0, 16)}...`)
this.log(`Autobase local key: ${this.autobase.local.key.toString('hex').substr(0, 16)}...`)
this.log(`Joined autobase: ${this.autobase.key.toString('hex').substr(0, 16)}...`)
this.log(`Initially writable: ${this.autobase.writable}`)
// Join swarm like FlockManager
const discovery = this.swarm.join(this.autobase.discoveryKey)
await discovery.flushed()
// Sync with existing autobase state to receive addWriter messages
await this.autobase.update()
this.log(`After initial sync - writable: ${this.autobase.writable}, inputs: ${this.autobase.inputs?.length || 0}`)
// Wait for autobase to become writable
if (this.resolveJoin) {
this.waitForWritable()
// Give networking time to establish before checking writable status
setTimeout(() => {
this.waitForWritable()
}, 2000)
}
}
waitForWritable() {
if (this.autobase.writable) {
this.log('βœ… Already writable, resolving join!')
this.resolveJoin()
return
}
this.log('⏳ Waiting for autobase to become writable...')
const check = () => {
if (this.autobase.writable) {
this.log('βœ… Now writable! Resolving join.')
this.autobase.off('update', check)
this.resolveJoin()
} else {
this.log(`⏳ Still not writable... inputs: ${this.autobase.inputs?.length || 0}`)
}
}
this.autobase.on('update', check)
}
async createInvite() {
const { invite, publicKey } = BlindPairing.createInvite(this.autobase.key)
this.invitePublicKey = publicKey
return z32.encode(invite)
}
async onAddMember(candidate) {
this.log(`Received join request from candidate: ${candidate.inviteId.toString('hex').substr(0, 16)}...`)
candidate.open(this.invitePublicKey)
const writerKey = candidate.userData
if (!writerKey) {
this.log('No userData received from candidate')
return
}
this.log(`Adding new writer: ${writerKey.toString('hex').substr(0, 16)}...`)
// Add the new peer as a writer to the autobase (convert Buffer to hex for JSON encoding)
await this.autobase.append({
type: 'addWriter',
key: writerKey.toString('hex'),
addedBy: this.name,
timestamp: Date.now()
})
// Update to sync the new writer
await this.autobase.update()
this.log(`Alice updated after adding writer - inputs: ${this.autobase.inputs?.length || 0}`)
// Confirm the pairing
candidate.confirm({
key: this.autobase.key,
encryptionKey: this.autobase.encryptionKey
})
this.log('New writer added successfully')
}
open(store) {
return store.get('messages', { valueEncoding: 'json' })
}
async apply(nodes, view, host) {
for (const node of nodes) {
const message = node.value
if (message.type === 'addWriter') {
this.log(`Adding writer: ${message.key.substr(0, 16)}... (by ${message.addedBy})`)
// Convert hex string back to Buffer
await host.addWriter(Buffer.from(message.key, 'hex'))
this.log(`Writer added. Now writable: ${this.autobase?.writable || false}`)
continue
}
// Store regular messages
if (message.type === 'request' || message.type === 'fulfillment') {
await view.append(message)
}
// Store all non-addWriter messages in view (like FlockManager does)
await view.append(message)
}
}
async processMessages() {
try {
// Force update to sync with other writers
await this.autobase.update()
if (!this.autobase.view) {
this.log('No view available yet')
return
}
this.log(`Processing messages, view length: ${this.autobase.view.length}, writable: ${this.autobase.writable}, inputs: ${this.autobase.inputs?.length || 0}`)
const length = this.autobase.view.length
for (let i = 0; i < length; i++) {
const message = await this.autobase.view.get(i)
if (!message) continue
const messageId = `${message.id || i}-${message.type}-${message.from}`
if (this.processedMessages.has(messageId)) continue
this.processedMessages.add(messageId)
await this.handleMessage(message)
}
} catch (error) {
this.log(`Error processing messages: ${error.message}`)
}
}
async handleMessage(message) {
switch (message.type) {
case 'request':
await this.handleRequest(message)
break
case 'fulfillment':
this.handleFulfillment(message)
break
}
}
async handleRequest(request) {
const { id, functionName, args, from } = request
if (from === this.name) return
this.log(`RPC request: ${functionName}(${args.join(', ')}) from ${from}`)
// Check if already fulfilled
const fulfilled = await this.findFulfillment(id)
if (fulfilled) {
this.log(`Already fulfilled by ${fulfilled.fulfilledBy}`)
return
}
// Random chance to fulfill
if (Math.random() > 0.5) {
this.log('Decided not to fulfill')
return
}
// Fulfill after delay
setTimeout(() => this.fulfillRequest(request), Math.random() * 1500 + 500)
}
async findFulfillment(requestId) {
if (!this.autobase.view) return null
const length = this.autobase.view.length
for (let i = 0; i < length; i++) {
const message = await this.autobase.view.get(i)
if (message && message.type === 'fulfillment' && message.requestId === requestId) {
return message
}
}
return null
}
async fulfillRequest(request) {
const { id, functionName, args, from } = request
// Double-check not fulfilled
const existing = await this.findFulfillment(id)
if (existing) {
this.log(`Race lost to ${existing.fulfilledBy}`)
return
}
this.log(`Fulfilling: ${functionName}(${args.join(', ')})`)
try {
if (!FUNCTIONS[functionName]) {
throw new Error(`Unknown function: ${functionName}`)
}
const result = FUNCTIONS[functionName](...args)
await this.autobase.append({
type: 'fulfillment',
id: crypto.randomBytes(16).toString('hex'),
requestId: id,
result,
success: true,
fulfilledBy: this.name,
originalRequester: from,
timestamp: Date.now()
})
this.stats.fulfilled++
this.log(`βœ… Fulfilled: ${JSON.stringify(result)}`)
} catch (error) {
await this.autobase.append({
type: 'fulfillment',
id: crypto.randomBytes(16).toString('hex'),
requestId: id,
error: error.message,
success: false,
fulfilledBy: this.name,
originalRequester: from,
timestamp: Date.now()
})
this.log(`❌ Failed: ${error.message}`)
}
}
handleFulfillment(fulfillment) {
const { requestId, result, error, success, fulfilledBy, originalRequester } = fulfillment
if (originalRequester !== this.name) return
if (this.pendingRequests.has(requestId)) {
const { resolve, reject, startTime } = this.pendingRequests.get(requestId)
const duration = Date.now() - startTime
if (success) {
this.log(`Got result from ${fulfilledBy} (${duration}ms): ${JSON.stringify(result)}`)
resolve(result)
} else {
this.log(`Got error from ${fulfilledBy}: ${error}`)
reject(new Error(error))
}
this.pendingRequests.delete(requestId)
this.stats.received++
}
}
async callFunction(functionName, ...args) {
if (!this.autobase.writable) {
this.log(`Cannot call function - writable: ${this.autobase.writable}, inputs: ${this.autobase.inputs?.length || 0}`)
throw new Error('Not a writer yet')
}
return new Promise(async (resolve, reject) => {
const requestId = crypto.randomBytes(16).toString('hex')
const request = {
type: 'request',
id: requestId,
functionName,
args,
from: this.name,
timestamp: Date.now()
}
this.pendingRequests.set(requestId, {
resolve,
reject,
startTime: Date.now()
})
setTimeout(() => {
if (this.pendingRequests.has(requestId)) {
this.pendingRequests.delete(requestId)
reject(new Error('Request timeout'))
}
}, 8000)
this.log(`Calling: ${functionName}(${args.join(', ')})`)
await this.autobase.append(request)
this.stats.sent++
})
}
async cleanup() {
if (this.member) await this.member.close()
await this.pairing.close()
await this.swarm.destroy()
await this.store.close()
}
}
class BlindPairingDemo {
constructor() {
this.peers = []
}
log(message) {
const time = new Date().toISOString().substr(11, 8)
console.log(`[${time}] 🎬 DEMO: ${message}`)
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async run() {
console.log('🌐 Distributed RPC with Blind-Pairing Multi-Writer')
console.log('===================================================')
console.log('This demo shows:')
console.log('1. Alice creates autobase and invite')
console.log('2. Bob and Charlie use invite to join securely')
console.log('3. All peers become writers automatically')
console.log('4. Distributed RPC calls work between all peers\n')
try {
// Create Alice (creator)
this.log('Creating Alice as creator...')
const alice = new BlindPairingRPCPeer('Alice', true)
await alice.start()
const invite = alice.invite
this.log(`Alice created invite: ${invite}`)
await this.sleep(2000)
// Create Bob and Charlie as joiners
this.log('Bob and Charlie joining with invite...')
const bob = new BlindPairingRPCPeer('Bob', false)
const charlie = new BlindPairingRPCPeer('Charlie', false)
this.peers = [alice, bob, charlie]
// Join simultaneously
await Promise.all([
bob.start(invite),
charlie.start(invite)
])
this.log('All peers connected, waiting for writer setup...')
// Wait for all peers to become writable
this.log('Waiting for all peers to become writable...')
let attempts = 0
while (attempts < 30) { // 30 second timeout
// Force updates on all peers
for (const peer of this.peers) {
if (peer.autobase) {
await peer.autobase.update()
}
}
const writableStatus = this.peers.map(p => `${p.name}:${p.autobase?.writable || false}(${p.autobase?.inputs?.length || 0})`).join(', ')
this.log(`Writable status: ${writableStatus}`)
if (this.peers.every(p => p.autobase?.writable)) {
this.log('βœ… All peers are now writable!')
break
}
await this.sleep(1000)
attempts++
}
if (attempts >= 30) {
this.log('⚠️ Timeout waiting for all peers to become writable')
}
// Test RPC calls
this.log('Testing distributed RPC calls...')
const requests = [
{ peer: alice, func: 'add', args: ['15', '25'] },
{ peer: bob, func: 'greet', args: ['World'] },
{ peer: charlie, func: 'fibonacci', args: ['7'] },
{ peer: alice, func: 'processData', args: ['test-data'] },
{ peer: bob, func: 'multiply', args: ['6', '7'] }
]
for (const req of requests) {
try {
const result = await req.peer.callFunction(req.func, ...req.args)
req.peer.log(`πŸŽ‰ Result: ${JSON.stringify(result)}`)
} catch (error) {
req.peer.log(`πŸ’₯ Failed: ${error.message}`)
}
await this.sleep(2000)
}
this.log('βœ… Blind-pairing RPC demo completed!')
this.log('Key features demonstrated:')
this.log('- Secure writer addition via blind-pairing')
this.log('- No manual key exchange needed')
this.log('- Automatic multi-writer setup')
this.log('- Distributed RPC with race conditions')
// Show stats
for (const peer of this.peers) {
console.log(` ${peer.name}: Sent=${peer.stats.sent}, Fulfilled=${peer.stats.fulfilled}, Received=${peer.stats.received}`)
}
} catch (error) {
this.log(`❌ Demo failed: ${error.message}`)
console.error(error)
} finally {
// Cleanup
for (const peer of this.peers) {
try {
await peer.cleanup()
} catch (e) {
// Ignore cleanup errors
}
}
// Clean up demo files
try {
const fs = require('fs')
const files = fs.readdirSync('.')
for (const file of files) {
if (file.startsWith('blind-rpc-')) {
fs.rmSync(file, { recursive: true, force: true })
}
}
} catch (e) {
// Ignore cleanup errors
}
process.exit(0)
}
}
}
if (require.main === module) {
const demo = new BlindPairingDemo()
demo.run().catch(console.error)
}
module.exports = BlindPairingRPCPeer
➜ distributed-rpc node blind-pairing-rpc.js
🌐 Distributed RPC with Blind-Pairing Multi-Writer
===================================================
This demo shows:
1. Alice creates autobase and invite
2. Bob and Charlie use invite to join securely
3. All peers become writers automatically
4. Distributed RPC calls work between all peers
[16:26:43] 🎬 DEMO: Creating Alice as creator...
[16:26:43] πŸ”΅ Alice: Starting as creator...
[16:26:43] πŸ”΅ Alice: Created autobase: d8a35450a2958ced...
[16:26:43] πŸ”΅ Alice: Adding writer: d8a35450a2958ced... (by Alice)
[16:26:43] πŸ”΅ Alice: Writer added. Now writable: true
[16:26:43] πŸ”΅ Alice: Added self as first writer - inputs: 0
[16:26:48] πŸ”΅ Alice: Invite created: yryss9yrwkag4nsa5s78jtqbq8s567mrhxux9psp389w8sxbfspxj3ccu8nna4rh1jeoatsk1ddsm9ecg9xci5bmdnbkcknr47a4b8scnc
[16:26:50] πŸ”΅ Alice: Processing messages, view length: 0, writable: true, inputs: 0
[16:26:50] 🎬 DEMO: Alice created invite: yryss9yrwkag4nsa5s78jtqbq8s567mrhxux9psp389w8sxbfspxj3ccu8nna4rh1jeoatsk1ddsm9ecg9xci5bmdnbkcknr47a4b8scnc
[16:26:52] 🎬 DEMO: Bob and Charlie joining with invite...
[16:26:52] 🟒 Bob: Joining with invite: yryss9yrwkag4nsa5s78jtqbq8s567mrhxux9psp389w8sxbfspxj3ccu8nna4rh1jeoatsk1ddsm9ecg9xci5bmdnbkcknr47a4b8scnc
[16:26:52] 🟣 Charlie: Joining with invite: yryss9yrwkag4nsa5s78jtqbq8s567mrhxux9psp389w8sxbfspxj3ccu8nna4rh1jeoatsk1ddsm9ecg9xci5bmdnbkcknr47a4b8scnc
[16:26:55] 🟒 Bob: Connected to peer, setting up replication
[16:26:55] πŸ”΅ Alice: Connected to peer, setting up replication
[16:26:55] πŸ”΅ Alice: Received join request from candidate: f94b867a638f2781...
[16:26:55] πŸ”΅ Alice: Adding new writer: 5bda8aff3fa2a126...
[16:26:55] πŸ”΅ Alice: Adding writer: 5bda8aff3fa2a126... (by Alice)
[16:26:55] πŸ”΅ Alice: Writer added. Now writable: true
[16:26:55] πŸ”΅ Alice: Processing messages, view length: 0, writable: true, inputs: 0
[16:26:55] πŸ”΅ Alice: Alice updated after adding writer - inputs: 0
[16:26:55] πŸ”΅ Alice: New writer added successfully
[16:26:55] 🟒 Bob: Join successful, creating autobase...
[16:26:55] 🟒 Bob: Local core key: 5bda8aff3fa2a126...
[16:26:55] 🟒 Bob: Autobase local key: 5bda8aff3fa2a126...
[16:26:55] 🟒 Bob: Joined autobase: d8a35450a2958ced...
[16:26:55] 🟒 Bob: Initially writable: false
[16:26:55] 🟒 Bob: Adding writer: d8a35450a2958ced... (by Alice)
[16:26:55] 🟒 Bob: Writer added. Now writable: false
[16:26:55] 🟒 Bob: Adding writer: 5bda8aff3fa2a126... (by Alice)
[16:26:55] 🟒 Bob: Writer added. Now writable: true
[16:26:55] 🟒 Bob: Adding writer: 5bda8aff3fa2a126... (by Alice)
[16:26:55] 🟒 Bob: Writer added. Now writable: true
[16:26:55] 🟣 Charlie: Connected to peer, setting up replication
[16:26:55] πŸ”΅ Alice: Connected to peer, setting up replication
[16:26:55] πŸ”΅ Alice: Received join request from candidate: f94b867a638f2781...
[16:26:55] πŸ”΅ Alice: Adding new writer: 7518f9756d5935a8...
[16:26:55] πŸ”΅ Alice: Adding writer: 7518f9756d5935a8... (by Alice)
[16:26:55] πŸ”΅ Alice: Writer added. Now writable: true
[16:26:55] πŸ”΅ Alice: Processing messages, view length: 0, writable: true, inputs: 0
[16:26:55] πŸ”΅ Alice: Alice updated after adding writer - inputs: 0
[16:26:55] πŸ”΅ Alice: New writer added successfully
[16:26:55] 🟣 Charlie: Join successful, creating autobase...
[16:26:55] 🟒 Bob: Adding writer: 7518f9756d5935a8... (by Alice)
[16:26:55] 🟒 Bob: Writer added. Now writable: true
[16:26:55] 🟣 Charlie: Local core key: 7518f9756d5935a8...
[16:26:55] 🟣 Charlie: Autobase local key: 7518f9756d5935a8...
[16:26:55] 🟣 Charlie: Joined autobase: d8a35450a2958ced...
[16:26:55] 🟣 Charlie: Initially writable: false
[16:26:55] 🟣 Charlie: Adding writer: d8a35450a2958ced... (by Alice)
[16:26:55] 🟣 Charlie: Writer added. Now writable: false
[16:26:55] 🟣 Charlie: Adding writer: 5bda8aff3fa2a126... (by Alice)
[16:26:55] 🟣 Charlie: Writer added. Now writable: false
[16:26:55] 🟣 Charlie: Adding writer: 7518f9756d5935a8... (by Alice)
[16:26:55] 🟣 Charlie: Writer added. Now writable: true
[16:26:55] 🟣 Charlie: Adding writer: 5bda8aff3fa2a126... (by Alice)
[16:26:55] 🟣 Charlie: Writer added. Now writable: false
[16:26:55] 🟣 Charlie: Adding writer: 7518f9756d5935a8... (by Alice)
[16:26:55] 🟣 Charlie: Writer added. Now writable: true
[16:26:59] 🟣 Charlie: After initial sync - writable: true, inputs: 0
[16:27:00] 🟒 Bob: After initial sync - writable: true, inputs: 0
[16:27:01] 🟒 Bob: Connected to peer, setting up replication
[16:27:01] 🟣 Charlie: Connected to peer, setting up replication
[16:27:01] πŸ”΅ Alice: Processing messages, view length: 0, writable: true, inputs: 0
[16:27:01] πŸ”΅ Alice: Processing messages, view length: 0, writable: true, inputs: 0
[16:27:01] 🟣 Charlie: βœ… Already writable, resolving join!
[16:27:01] 🟣 Charlie: Processing messages, view length: 0, writable: true, inputs: 0
[16:27:02] 🟒 Bob: βœ… Already writable, resolving join!
[16:27:02] 🟒 Bob: Processing messages, view length: 0, writable: true, inputs: 0
[16:27:02] 🎬 DEMO: All peers connected, waiting for writer setup...
[16:27:02] 🎬 DEMO: Waiting for all peers to become writable...
[16:27:02] 🎬 DEMO: Writable status: Alice:true(0), Bob:true(0), Charlie:true(0)
[16:27:02] 🎬 DEMO: βœ… All peers are now writable!
[16:27:02] 🎬 DEMO: Testing distributed RPC calls...
[16:27:02] πŸ”΅ Alice: Calling: add(15, 25)
[16:27:02] πŸ”΅ Alice: Processing messages, view length: 1, writable: true, inputs: 0
[16:27:02] 🟒 Bob: Processing messages, view length: 1, writable: true, inputs: 0
[16:27:02] 🟒 Bob: RPC request: add(15, 25) from Alice
[16:27:02] 🟣 Charlie: Processing messages, view length: 1, writable: true, inputs: 0
[16:27:02] 🟒 Bob: Decided not to fulfill
[16:27:02] 🟣 Charlie: RPC request: add(15, 25) from Alice
[16:27:02] 🟣 Charlie: Decided not to fulfill
[16:27:10] πŸ”΅ Alice: πŸ’₯ Failed: Request timeout
[16:27:12] 🟒 Bob: Calling: greet(World)
[16:27:12] 🟒 Bob: Processing messages, view length: 2, writable: true, inputs: 0
[16:27:12] 🟣 Charlie: Processing messages, view length: 2, writable: true, inputs: 0
[16:27:12] 🟣 Charlie: RPC request: greet(World) from Bob
[16:27:12] πŸ”΅ Alice: Processing messages, view length: 2, writable: true, inputs: 0
[16:27:12] 🟣 Charlie: Decided not to fulfill
[16:27:12] πŸ”΅ Alice: RPC request: greet(World) from Bob
[16:27:12] πŸ”΅ Alice: Decided not to fulfill
[16:27:20] 🟒 Bob: πŸ’₯ Failed: Request timeout
[16:27:22] 🟣 Charlie: Calling: fibonacci(7)
[16:27:22] 🟣 Charlie: Processing messages, view length: 3, writable: true, inputs: 0
[16:27:22] πŸ”΅ Alice: Processing messages, view length: 3, writable: true, inputs: 0
[16:27:22] 🟒 Bob: Processing messages, view length: 3, writable: true, inputs: 0
[16:27:22] πŸ”΅ Alice: RPC request: fibonacci(7) from Charlie
[16:27:22] 🟒 Bob: RPC request: fibonacci(7) from Charlie
[16:27:22] 🟒 Bob: Decided not to fulfill
[16:27:24] πŸ”΅ Alice: Fulfilling: fibonacci(7)
[16:27:24] πŸ”΅ Alice: βœ… Fulfilled: 13
[16:27:24] πŸ”΅ Alice: Processing messages, view length: 4, writable: true, inputs: 0
[16:27:24] 🟣 Charlie: Processing messages, view length: 4, writable: true, inputs: 0
[16:27:24] 🟒 Bob: Processing messages, view length: 4, writable: true, inputs: 0
[16:27:24] 🟣 Charlie: Got result from Alice (1944ms): 13
[16:27:24] 🟣 Charlie: πŸŽ‰ Result: 13
[16:27:26] πŸ”΅ Alice: Calling: processData(test-data)
[16:27:26] πŸ”΅ Alice: Processing messages, view length: 5, writable: true, inputs: 0
[16:27:26] 🟒 Bob: Processing messages, view length: 5, writable: true, inputs: 0
[16:27:26] 🟣 Charlie: Processing messages, view length: 5, writable: true, inputs: 0
[16:27:26] 🟒 Bob: RPC request: processData(test-data) from Alice
[16:27:26] 🟣 Charlie: RPC request: processData(test-data) from Alice
[16:27:26] 🟣 Charlie: Fulfilling: processData(test-data)
[16:27:26] 🟣 Charlie: βœ… Fulfilled: {"processed":true,"length":9,"uppercase":"TEST-DATA","processedBy":20442}
[16:27:26] 🟣 Charlie: Processing messages, view length: 6, writable: true, inputs: 0
[16:27:26] 🟣 Charlie: Processing messages, view length: 6, writable: true, inputs: 0
[16:27:26] πŸ”΅ Alice: Processing messages, view length: 6, writable: true, inputs: 0
[16:27:26] 🟒 Bob: Processing messages, view length: 6, writable: true, inputs: 0
[16:27:26] πŸ”΅ Alice: Got result from Charlie (536ms): {"processed":true,"length":9,"uppercase":"TEST-DATA","processedBy":20442}
[16:27:26] πŸ”΅ Alice: πŸŽ‰ Result: {"processed":true,"length":9,"uppercase":"TEST-DATA","processedBy":20442}
[16:27:27] 🟒 Bob: Race lost to Charlie
[16:27:28] 🟒 Bob: Calling: multiply(6, 7)
[16:27:28] 🟒 Bob: Processing messages, view length: 7, writable: true, inputs: 0
[16:27:28] 🟣 Charlie: Processing messages, view length: 7, writable: true, inputs: 0
[16:27:28] πŸ”΅ Alice: Processing messages, view length: 7, writable: true, inputs: 0
[16:27:28] 🟣 Charlie: RPC request: multiply(6, 7) from Bob
[16:27:28] πŸ”΅ Alice: RPC request: multiply(6, 7) from Bob
[16:27:28] πŸ”΅ Alice: Decided not to fulfill
[16:27:29] 🟣 Charlie: Fulfilling: multiply(6, 7)
[16:27:29] 🟣 Charlie: βœ… Fulfilled: 42
[16:27:29] 🟣 Charlie: Processing messages, view length: 8, writable: true, inputs: 0
[16:27:29] 🟣 Charlie: Processing messages, view length: 8, writable: true, inputs: 0
[16:27:29] πŸ”΅ Alice: Processing messages, view length: 8, writable: true, inputs: 0
[16:27:29] 🟒 Bob: Processing messages, view length: 8, writable: true, inputs: 0
[16:27:29] 🟒 Bob: Got result from Charlie (927ms): 42
[16:27:29] 🟒 Bob: πŸŽ‰ Result: 42
[16:27:31] 🎬 DEMO: βœ… Blind-pairing RPC demo completed!
[16:27:31] 🎬 DEMO: Key features demonstrated:
[16:27:31] 🎬 DEMO: - Secure writer addition via blind-pairing
[16:27:31] 🎬 DEMO: - No manual key exchange needed
[16:27:31] 🎬 DEMO: - Automatic multi-writer setup
[16:27:31] 🎬 DEMO: - Distributed RPC with race conditions
Alice: Sent=2, Fulfilled=1, Received=1
Bob: Sent=2, Fulfilled=0, Received=1
Charlie: Sent=1, Fulfilled=2, Received=1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment