@sschepis/resolang
Version:
ResoLang - Core quantum resonance computation library for browser and Node.js
574 lines • 19.5 kB
JavaScript
// ResoLang Contract Service
// Handles contract deployment, storage, execution, and consensus
import { tokenize } from './lexer.js';
import { validate, ResoLangInterpreter } from './interpreter.js';
import { zeroPrimeValue, } from './economy.js';
// =============================================================================
// CONTRACT PARSER
// =============================================================================
/**
* Parse a ResoLang source file to extract contract definition
*/
export function parseContract(source) {
// First validate the source
const validation = validate(source);
if (!validation.valid) {
return { error: validation.errors.map(e => e.message).join('; ') };
}
const tokens = tokenize(source);
let current = 0;
// Helper functions
const isAtEnd = () => tokens[current]?.type === 'EOF';
const peek = () => tokens[current];
const advance = () => tokens[current++];
const check = (type) => !isAtEnd() && peek().type === type;
const match = (type) => {
if (check(type)) {
advance();
return true;
}
return false;
};
// Find PROGRAM
while (!isAtEnd() && !check('PROGRAM'))
advance();
if (!match('PROGRAM')) {
return { error: 'No PROGRAM block found' };
}
// Get program name
if (!check('IDENTIFIER')) {
return { error: 'Expected program name after PROGRAM' };
}
const name = advance().value;
// Parse the program body
const config = {};
const stateVariables = [];
const methods = [];
// Skip to opening brace
while (!isAtEnd() && !check('LBRACE'))
advance();
if (!match('LBRACE')) {
return { error: 'Expected { after program name' };
}
let braceDepth = 1;
while (braceDepth > 0 && !isAtEnd()) {
const token = peek();
switch (token.type) {
case 'CONFIG':
advance();
if (match('LBRACE')) {
// Parse config block
while (!check('RBRACE') && !isAtEnd()) {
if (check('IDENTIFIER')) {
const key = advance().value;
if (match('COLON')) {
const value = parseValue();
config[key] = value;
}
}
if (check('COMMA'))
advance();
}
match('RBRACE');
}
break;
case 'STATE':
advance();
if (match('LBRACE')) {
// Parse state block
while (!check('RBRACE') && !isAtEnd()) {
if (isTypeKeyword(peek().type)) {
const varType = advance().value;
if (check('IDENTIFIER')) {
const varName = advance().value;
if (match('ASSIGN')) {
const initialValue = parseValue();
stateVariables.push({ name: varName, varType, initialValue });
}
}
}
else {
advance();
}
}
match('RBRACE');
}
break;
case 'FUNCTION': {
const method = parseMethod();
if (method)
methods.push(method);
break;
}
case 'LBRACE':
advance();
braceDepth++;
break;
case 'RBRACE':
advance();
braceDepth--;
break;
default:
advance();
}
}
// Compute code hash
const codeHash = computeCodeHash(source);
return {
name,
config,
stateVariables,
methods,
sourceCode: source,
codeHash,
};
// Helper: check if token is a type keyword
function isTypeKeyword(type) {
return ['INT', 'FLOAT', 'COMPLEX', 'BOOLEAN', 'STRING', 'QUATERNION', 'PRIMES', 'RESONANCE', 'SUPERPOSITION'].includes(type);
}
// Helper: parse a value (simplified)
function parseValue() {
const token = peek();
if (token.type === 'NUMBER') {
advance();
return parseFloat(token.value);
}
if (token.type === 'STRING_LITERAL') {
advance();
return token.value;
}
if (token.type === 'BOOLEAN_LITERAL') {
advance();
return token.value === 'true';
}
if (token.type === 'LBRACKET') {
// Array
advance();
const items = [];
while (!check('RBRACKET') && !isAtEnd()) {
items.push(parseValue());
if (check('COMMA'))
advance();
}
match('RBRACKET');
return items;
}
if (token.type === 'LBRACE') {
// Object/Map
advance();
const obj = {};
while (!check('RBRACE') && !isAtEnd()) {
if (check('IDENTIFIER') || check('STRING_LITERAL')) {
const key = advance().value;
if (match('COLON')) {
obj[key] = parseValue();
}
}
if (check('COMMA'))
advance();
}
match('RBRACE');
return obj;
}
if (check('IDENTIFIER')) {
advance();
return token.value;
}
advance();
return null;
}
// Helper: parse a method
function parseMethod() {
const startLine = peek().line;
advance(); // FUNCTION
if (!check('IDENTIFIER'))
return null;
const methodName = advance().value;
// Parse parameters
const params = [];
if (match('LPAREN')) {
while (!check('RPAREN') && !isAtEnd()) {
if (check('IDENTIFIER')) {
const paramName = advance().value;
let paramType;
if (match('COLON') && check('IDENTIFIER')) {
paramType = advance().value;
}
params.push({ name: paramName, paramType });
}
if (check('COMMA'))
advance();
}
match('RPAREN');
}
// Parse return type
let returnType;
if (match('ARROW')) {
if (check('IDENTIFIER') || isTypeKeyword(peek().type)) {
returnType = advance().value;
}
}
// Find method body
let isSync = false;
let isConsensus = false;
const bodyStart = current;
// Skip to body
while (!check('LBRACE') && !isAtEnd()) {
advance();
}
if (!match('LBRACE'))
return null;
// Check for SYNC or CONSENSUS wrapper
if (check('SYNC')) {
isSync = true;
advance();
if (match('LBRACE')) {
// Body is inside SYNC block
}
}
else if (check('CONSENSUS')) {
isConsensus = true;
advance();
// Skip consensus params
if (match('LPAREN')) {
while (!check('RPAREN') && !isAtEnd())
advance();
match('RPAREN');
}
if (match('LBRACE')) {
// Body is inside CONSENSUS block
}
}
// Find end of method body
let bodyDepth = 1;
const bodyTokens = [];
while (bodyDepth > 0 && !isAtEnd()) {
const t = peek();
if (t.type === 'LBRACE')
bodyDepth++;
if (t.type === 'RBRACE')
bodyDepth--;
if (bodyDepth > 0)
bodyTokens.push(t);
advance();
}
const endLine = peek().line;
// Reconstruct body source (simplified)
const body = bodyTokens.map(t => t.value).join(' ');
return {
name: methodName,
params,
returnType,
isSync,
isConsensus,
body,
startLine,
endLine,
};
}
}
/**
* Compute hash of contract source code
*/
function computeCodeHash(source) {
let hash = 0n;
for (let i = 0; i < source.length; i++) {
hash = ((hash << 5n) - hash + BigInt(source.charCodeAt(i))) & 0xffffffffffffffffn;
}
return `CH-${hash.toString(36).toUpperCase()}`;
}
/**
* Compute contract address from code hash and deployer
*/
function computeContractAddress(codeHash, deployer, nonce) {
const combined = `${codeHash}:${deployer}:${nonce}`;
let hash = 0n;
for (let i = 0; i < combined.length; i++) {
hash = ((hash << 5n) - hash + BigInt(combined.charCodeAt(i))) & 0xffffffffffffffffn;
}
return `CA-${hash.toString(36).toUpperCase().slice(0, 16)}`;
}
// =============================================================================
// CONTRACT DEPLOYMENT
// =============================================================================
export class ContractDeploymentService {
contracts = new Map();
interpreter;
constructor() {
this.interpreter = new ResoLangInterpreter();
}
/**
* Deploy a new contract
*/
deploy(source, deployer, deployerSignature, initialBalance, deployerNonce = 0n) {
// Parse the contract
const parseResult = parseContract(source);
if ('error' in parseResult) {
return { error: `Parse error: ${parseResult.error}` };
}
const definition = parseResult;
// Compute contract address
const address = computeContractAddress(definition.codeHash, deployer, deployerNonce);
// Check if address already exists
if (this.contracts.has(address)) {
return { error: `Contract already exists at address ${address}` };
}
// Initialize state
const state = new Map();
for (const sv of definition.stateVariables) {
state.set(sv.name, sv.initialValue);
}
// Compute initial state root
const stateRoot = this.computeStateRoot(state);
// Create deployed contract
const contract = {
id: `CONTRACT-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
address,
definition,
state,
stateRoot,
balance: initialBalance || zeroPrimeValue(),
deployer,
deployerSignature,
createdAt: Date.now(),
callCount: 0,
totalGasUsed: 0,
version: 1,
status: 'active',
};
this.contracts.set(address, contract);
return contract;
}
/**
* Get a deployed contract
*/
getContract(address) {
return this.contracts.get(address);
}
/**
* List all deployed contracts
*/
listContracts() {
return Array.from(this.contracts.values());
}
/**
* Call a contract method
*/
call(contractAddress, method, args, caller, value, gasLimit = 100000) {
const startTime = performance.now();
const contract = this.contracts.get(contractAddress);
if (!contract) {
return {
success: false,
gasUsed: 0,
transfers: [],
stateChanges: [],
logs: [],
error: `Contract not found: ${contractAddress}`,
executionTime: 0,
};
}
if (contract.status !== 'active') {
return {
success: false,
gasUsed: 0,
transfers: [],
stateChanges: [],
logs: [],
error: `Contract is ${contract.status}`,
executionTime: 0,
};
}
// Find the method
const methodDef = contract.definition.methods.find(m => m.name === method);
if (!methodDef) {
return {
success: false,
gasUsed: 0,
transfers: [],
stateChanges: [],
logs: [],
error: `Method not found: ${method}`,
executionTime: 0,
};
}
// Build execution context
const callSource = this.buildCallSource(contract, methodDef, args, value);
// Set up interpreter with economy context
this.interpreter.setEconomyContext(caller, contractAddress);
// Execute with gas limit
const result = this.interpreter.execute(callSource, { gasLimit });
// Get transfers from result or interpreter
const transfers = result.transfers || this.interpreter.getTransfers();
// Get gas used from the interpreter's gas meter
const gasUsed = result.gasUsed ?? 0;
// Collect state changes
const stateChanges = [];
// Update contract state from execution results
for (const [key, values] of Object.entries(result.results)) {
const oldValue = contract.state.get(key);
const newValue = values[values.length - 1]; // Take last value
if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
stateChanges.push({ key, oldValue: oldValue ?? null, newValue });
contract.state.set(key, newValue);
}
}
// Update contract metadata
contract.callCount++;
contract.totalGasUsed += gasUsed;
contract.lastExecutedAt = Date.now();
contract.stateRoot = this.computeStateRoot(contract.state);
// If value was sent, add to contract balance
if (value) {
contract.balance = {
...contract.balance,
factors: new Map([...contract.balance.factors, ...value.factors]),
};
}
return {
success: result.success,
returnValue: result.results['return']?.[0],
gasUsed,
transfers,
stateChanges,
logs: result.output,
error: result.error,
executionTime: performance.now() - startTime,
};
}
/**
* Build ResoLang source for a method call
*/
buildCallSource(contract, method, args, value) {
const lines = [];
// Set up state variables
lines.push(`PROGRAM ${contract.definition.name}_call {`);
// Config
lines.push(' CONFIG {');
for (const [key, val] of Object.entries(contract.definition.config)) {
lines.push(` ${key}: ${JSON.stringify(val)},`);
}
lines.push(' }');
// State (current values)
lines.push(' STATE {');
for (const sv of contract.definition.stateVariables) {
const currentValue = contract.state.get(sv.name) ?? sv.initialValue;
lines.push(` ${sv.varType} ${sv.name} = ${this.valueToSource(currentValue)}`);
}
lines.push(' }');
// Execute block that calls the method
lines.push(' EXECUTE {');
// Set up arguments as variables
method.params.forEach((param, i) => {
const argValue = args[i] ?? null;
lines.push(` ${param.paramType || 'INT'} ${param.name} = ${this.valueToSource(argValue)}`);
});
// If value was sent
if (value) {
lines.push(` PRIMES _value = ${this.valueToSource(Array.from(value.factors.keys()))}`);
}
// Method body
lines.push(` // Method: ${method.name}`);
lines.push(` ${method.body}`);
// Store return value if any
if (method.returnType) {
lines.push(' STORE_RESULT("return", result)');
}
// Store updated state
for (const sv of contract.definition.stateVariables) {
lines.push(` STORE_RESULT("${sv.name}", ${sv.name})`);
}
lines.push(' }');
lines.push('}');
return lines.join('\n');
}
/**
* Convert a value to ResoLang source
*/
valueToSource(value) {
if (value === null)
return 'null';
if (typeof value === 'number')
return value.toString();
if (typeof value === 'string')
return `"${value}"`;
if (typeof value === 'boolean')
return value.toString();
if (Array.isArray(value)) {
return `[${value.map(v => this.valueToSource(v)).join(', ')}]`;
}
if (typeof value === 'object') {
const entries = Object.entries(value)
.map(([k, v]) => `${k}: ${this.valueToSource(v)}`);
return `{ ${entries.join(', ')} }`;
}
return String(value);
}
/**
* Compute state root (prime product of all state hashes)
*/
computeStateRoot(state) {
let root = 1n;
const primes = [2n, 3n, 5n, 7n, 11n, 13n, 17n, 19n, 23n, 29n];
let primeIndex = 0;
for (const [key, value] of state) {
const valueHash = this.hashValue(value);
const prime = primes[primeIndex % primes.length];
root *= prime ** (valueHash % 10n + 1n);
primeIndex++;
}
return root;
}
/**
* Hash a value to a bigint
*/
hashValue(value) {
const str = JSON.stringify(value);
let hash = 0n;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5n) - hash + BigInt(str.charCodeAt(i))) & 0xffffffffn;
}
return hash;
}
/**
* Pause a contract
*/
pause(contractAddress, caller) {
const contract = this.contracts.get(contractAddress);
if (!contract)
return false;
if (contract.deployer !== caller)
return false;
contract.status = 'paused';
return true;
}
/**
* Resume a paused contract
*/
resume(contractAddress, caller) {
const contract = this.contracts.get(contractAddress);
if (!contract)
return false;
if (contract.deployer !== caller)
return false;
if (contract.status !== 'paused')
return false;
contract.status = 'active';
return true;
}
/**
* Get contract statistics
*/
getStats() {
const contracts = Array.from(this.contracts.values());
return {
totalContracts: contracts.length,
activeContracts: contracts.filter(c => c.status === 'active').length,
totalCalls: contracts.reduce((sum, c) => sum + c.callCount, 0),
totalGasUsed: contracts.reduce((sum, c) => sum + c.totalGasUsed, 0),
};
}
}
// Export singleton
export const contractService = new ContractDeploymentService();
//# sourceMappingURL=contracts.js.map