TL;DR
We’re officially unveiling the state synchronization message format for OIP v0.1. This comprehensive specification includes JSON Schema-based validation systems alongside practical implementations in TypeScript, Rust, and Go. We share the technical challenges we faced during development, particularly around cross-chain identity management and regulatory action standardization, while demonstrating how the OCID (Oracle Contract ID) system achieves seamless on-chain and off-chain identity integration through real-world code implementations.
After months of research and dozens of iterative design cycles, we’re finally ready to unveil the state synchronization message format for Oracle Interoperability Protocol (OIP) v0.1. This isn’t just another message specification—it’s the core implementation of what Oraclizer calls complete state synchronization.
The most challenging question our development team grappled with was: “How do we represent the complex states of both off-chain and on-chain systems in a single message format?” While traditional oracles have been limited to simple key-value data transmission, we needed to design a format that encompasses regulatory compliance, identity management, and asset states—all within a unified structure.
Design Philosophy of OIP Message Architecture
If traditional oracle messages focused on unidirectional data pushing, the OIP message format is designed with bidirectional state synchronization as its foundation. The core consists of a hierarchical structure organized into three layers.
The Identity Layer integrates on-chain and off-chain identities through the OCID system. The State Layer represents the current states of assets and contracts, while the Compliance Layer manages regulatory requirements and enforcement actions.
This design reflects our fundamental approach to ensuring state completeness rather than simply transmitting data. It’s not about moving information from point A to point B—it’s about maintaining perfect synchronization across distributed systems while preserving regulatory integrity.
JSON Schema-Based Message Validation System
OIP v0.1’s message validation is implemented based on JSON Schema Draft 7. This is a comprehensive validation system that goes beyond simple format verification to include regulatory compliance.
{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://docs.oraclizer.io/schema/oip/v0.1/message.json", "title": "OIP State Synchronization Message", "type": "object", "required": [ "version", "messageType", "timestamp", "ocid", "payload", "signature" ], "properties": { "version": { "type": "string", "const": "0.1.0", "description": "OIP protocol version" }, "messageType": { "type": "string", "enum": [ "ASSET_UPDATE", "CONTRACT_STATE", "REGULATORY_ACTION", "IDENTITY_SYNC", "CROSS_CHAIN_BRIDGE" ] }, "timestamp": { "type": "integer", "minimum": 1640995200, "description": "Unix timestamp in seconds" }, "ocid": { "$ref": "#/definitions/OCID" }, "payload": { "oneOf": [ {"$ref": "#/definitions/AssetPayload"}, {"$ref": "#/definitions/ContractPayload"}, {"$ref": "#/definitions/RegulatoryPayload"} ] }, "signature": { "$ref": "#/definitions/Signature" } }, "definitions": { "OCID": { "type": "object", "required": ["damlPartyId", "zkId", "chainId"], "properties": { "damlPartyId": { "type": "string", "pattern": "^[a-zA-Z0-9_-]+$", "minLength": 1, "maxLength": 128 }, "zkId": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$", "description": "Zero-knowledge proof based identity hash" }, "chainId": { "type": "string", "pattern": "^[1-9][0-9]*$", "description": "EIP-155 chain identifier" } } } } }
Important Note: This schema is in active development and will undergo additional optimization during testnet deployment.
TypeScript Implementation: Type Safety and Developer Experience
For OIP integration in frontend and Node.js environments, we provide TypeScript type definitions and validation libraries that prioritize developer experience and type safety.
// types/oip.ts export interface OIPMessage { version: "0.1.0"; messageType: MessageType; timestamp: number; ocid: OCID; payload: MessagePayload; signature: Signature; metadata?: MessageMetadata; } export enum MessageType { ASSET_UPDATE = "ASSET_UPDATE", CONTRACT_STATE = "CONTRACT_STATE", REGULATORY_ACTION = "REGULATORY_ACTION", IDENTITY_SYNC = "IDENTITY_SYNC", CROSS_CHAIN_BRIDGE = "CROSS_CHAIN_BRIDGE" } export interface OCID { damlPartyId: string; zkId: string; // 0x prefixed hex string chainId: string; } export interface AssetPayload { assetId: string; assetType: AssetType; balance: string; // Using string for precise decimal handling lockStatus: LockStatus; lockExpiration?: number; regulatoryAuthority?: RegulatoryAuthority; } export enum AssetType { ERC20 = "ERC20", ERC721 = "ERC721", ERC1400 = "ERC1400", BOND = "BOND", STOCK = "STOCK", REAL_ESTATE = "REAL_ESTATE", CARBON_CREDIT = "CARBON_CREDIT" } export enum LockStatus { UNLOCKED = "UNLOCKED", TEMP_LOCKED = "TEMP_LOCKED", PERM_LOCKED = "PERM_LOCKED", REGULATORY_LOCKED = "REGULATORY_LOCKED" } // Message validation with Ajv import Ajv from "ajv"; import { OIPMessage } from "./types/oip"; export class OIPMessageValidator { private ajv: Ajv; private schema: object; constructor() { this.ajv = new Ajv({ strict: true, validateFormats: true }); this.schema = require("./schema/oip-message.json"); } validate(message: unknown): message is OIPMessage { const isValid = this.ajv.validate(this.schema, message); if (!isValid) { console.error("OIP Message validation failed:", this.ajv.errors); return false; } return true; } validateAndParse(rawMessage: string): OIPMessage | null { try { const parsed = JSON.parse(rawMessage); return this.validate(parsed) ? parsed as OIPMessage : null; } catch (error) { console.error("Failed to parse OIP message:", error); return null; } } } // Usage example const validator = new OIPMessageValidator(); const message: OIPMessage = { version: "0.1.0", messageType: MessageType.ASSET_UPDATE, timestamp: Math.floor(Date.now() / 1000), ocid: { damlPartyId: "alice_bank_001", zkId: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", chainId: "1" }, payload: { assetId: "US_TREASURY_001", assetType: AssetType.BOND, balance: "1000000.00", lockStatus: LockStatus.UNLOCKED }, signature: { algorithm: "ECDSA_secp256k1", publicKey: "0x...", signature: "0x..." } };
Rust Implementation: Performance and Memory Safety
The Oraclizer core nodes are implemented in Rust, and our OIP message processing leverages Rust’s type system and performance capabilities to their fullest extent.
// src/oip/message.rs use serde::{Deserialize, Serialize}; use std::collections::HashMap; use chrono::{DateTime, Utc}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OIPMessage { pub version: String, pub message_type: MessageType, pub timestamp: i64, pub ocid: OCID, pub payload: MessagePayload, pub signature: Signature, #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum MessageType { AssetUpdate, ContractState, RegulatoryAction, IdentitySync, CrossChainBridge, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OCID { pub daml_party_id: String, pub zk_id: String, pub chain_id: String, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type", content = "data")] pub enum MessagePayload { Asset(AssetPayload), Contract(ContractPayload), Regulatory(RegulatoryPayload), } // Message processing implementation impl OIPMessage { pub fn new( message_type: MessageType, ocid: OCID, payload: MessagePayload, ) -> Self { Self { version: "0.1.0".to_string(), message_type, timestamp: Utc::now().timestamp(), ocid, payload, signature: Signature::default(), // To be signed later metadata: None, } } pub fn validate(&self) -> Result<(), ValidationError> { // Version validation if self.version != "0.1.0" { return Err(ValidationError::UnsupportedVersion(self.version.clone())); } // OCID validation self.ocid.validate()?; // Payload validation based on message type match (&self.message_type, &self.payload) { (MessageType::AssetUpdate, MessagePayload::Asset(_)) => Ok(()), (MessageType::ContractState, MessagePayload::Contract(_)) => Ok(()), (MessageType::RegulatoryAction, MessagePayload::Regulatory(_)) => Ok(()), _ => Err(ValidationError::PayloadMismatch), } } pub fn sign(&mut self, private_key: &str) -> Result<(), SignatureError> { let message_hash = self.compute_hash()?; self.signature = Signature::sign(message_hash, private_key)?; Ok(()) } fn compute_hash(&self) -> Result<[u8; 32], HashError> { use sha2::{Sha256, Digest}; let mut hasher = Sha256::new(); // Hash all fields except signature hasher.update(self.version.as_bytes()); hasher.update(&[self.message_type as u8]); hasher.update(&self.timestamp.to_be_bytes()); hasher.update(serde_json::to_string(&self.ocid)?.as_bytes()); hasher.update(serde_json::to_string(&self.payload)?.as_bytes()); Ok(hasher.finalize().into()) } } // OCID implementation with validation impl OCID { pub fn validate(&self) -> Result<(), ValidationError> { // DAML Party ID validation if self.daml_party_id.is_empty() || self.daml_party_id.len() > 128 { return Err(ValidationError::InvalidDAMLPartyId); } // ZK ID validation (must be 32-byte hex string with 0x prefix) if !self.zk_id.starts_with("0x") || self.zk_id.len() != 66 { return Err(ValidationError::InvalidZKId); } // Chain ID validation (must be positive integer string) if self.chain_id.parse::().is_err() { return Err(ValidationError::InvalidChainId); } Ok(()) } pub fn to_unified_identity(&self) -> String { format!("{}:{}:{}", self.chain_id, self.daml_party_id, &self.zk_id[..10]) } } #[derive(Debug, thiserror::Error)] pub enum ValidationError { #[error("Unsupported version: {0}")] UnsupportedVersion(String), #[error("Invalid DAML Party ID")] InvalidDAMLPartyId, #[error("Invalid ZK ID format")] InvalidZKId, #[error("Invalid Chain ID")] InvalidChainId, #[error("Payload type mismatch with message type")] PayloadMismatch, }
Go Implementation: High-Performance Network Processing
We also provide a Go implementation for cross-chain bridges and inter-node communication. Go’s concurrency model is perfectly optimized for handling large volumes of state synchronization messages.
// pkg/oip/message.go package oip import ( "crypto/sha256" "encoding/json" "fmt" "regexp" "strconv" "time" ) type OIPMessage struct { Version string `json:"version"` MessageType MessageType `json:"messageType"` Timestamp int64 `json:"timestamp"` OCID OCID `json:"ocid"` Payload json.RawMessage `json:"payload"` Signature Signature `json:"signature"` Metadata *MessageMetadata `json:"metadata,omitempty"` } type MessageType string const ( AssetUpdate MessageType = "ASSET_UPDATE" ContractState MessageType = "CONTRACT_STATE" RegulatoryAction MessageType = "REGULATORY_ACTION" IdentitySync MessageType = "IDENTITY_SYNC" CrossChainBridge MessageType = "CROSS_CHAIN_BRIDGE" ) type OCID struct { DAMLPartyID string `json:"damlPartyId"` ZKId string `json:"zkId"` ChainID string `json:"chainId"` } // Validation patterns var ( zkIdPattern = regexp.MustCompile(`^0x[a-fA-F0-9]{64}$`) chainIdPattern = regexp.MustCompile(`^[1-9][0-9]*$`) partyIdPattern = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`) ) func NewOIPMessage(msgType MessageType, ocid OCID, payload interface{}) (*OIPMessage, error) { payloadBytes, err := json.Marshal(payload) if err != nil { return nil, fmt.Errorf("failed to marshal payload: %w", err) } return &OIPMessage{ Version: "0.1.0", MessageType: msgType, Timestamp: time.Now().Unix(), OCID: ocid, Payload: payloadBytes, Signature: Signature{}, // To be signed later }, nil } func (m *OIPMessage) Validate() error { // Version validation if m.Version != "0.1.0" { return fmt.Errorf("unsupported version: %s", m.Version) } // Timestamp validation (not too old, not in future) now := time.Now().Unix() if m.Timestamp < now-300 || m.Timestamp > now+60 { // 5 min past, 1 min future return fmt.Errorf("invalid timestamp: %d", m.Timestamp) } // OCID validation if err := m.OCID.Validate(); err != nil { return fmt.Errorf("invalid OCID: %w", err) } // Payload validation if len(m.Payload) == 0 { return fmt.Errorf("empty payload") } return nil } func (ocid *OCID) Validate() error { // DAML Party ID validation if len(ocid.DAMLPartyID) == 0 || len(ocid.DAMLPartyID) > 128 { return fmt.Errorf("invalid DAML Party ID length") } if !partyIdPattern.MatchString(ocid.DAMLPartyID) { return fmt.Errorf("invalid DAML Party ID format") } // ZK ID validation if !zkIdPattern.MatchString(ocid.ZKId) { return fmt.Errorf("invalid ZK ID format") } // Chain ID validation if !chainIdPattern.MatchString(ocid.ChainID) { return fmt.Errorf("invalid Chain ID format") } if _, err := strconv.ParseUint(ocid.ChainID, 10, 64); err != nil { return fmt.Errorf("chain ID must be valid integer: %w", err) } return nil } func (m *OIPMessage) ComputeHash() ([32]byte, error) { hasher := sha256.New() // Hash version hasher.Write([]byte(m.Version)) // Hash message type hasher.Write([]byte(m.MessageType)) // Hash timestamp timestampBytes := make([]byte, 8) for i := 0; i < 8; i++ { timestampBytes[i] = byte(m.Timestamp >> (8 * (7 - i))) } hasher.Write(timestampBytes) // Hash OCID ocidBytes, err := json.Marshal(m.OCID) if err != nil { return [32]byte{}, err } hasher.Write(ocidBytes) // Hash payload hasher.Write(m.Payload) return sha256.Sum256(hasher.Sum(nil)), nil }
Regulatory Action Standardization: The Complexity of Real Implementation
The most challenging aspect of the OIP message format was standardizing Regulatory Actions. The process of implementing the six actions defined in RCP (FREEZE, SEIZE, CONFISCATE, LIQUIDATE, RESTRICT, RECOVER) as message formats revealed unexpected complexities we hadn’t anticipated.
// Regulatory Action Implementation export interface RegulatoryPayload { actionType: RegulatoryActionType; authority: RegulatoryAuthority; targetAssets: string[]; targetContracts?: string[]; actionTimestamp: number; expirationTimestamp?: number; // For temporary actions legalReference: LegalReference; executionParameters: ActionParameters; } export enum RegulatoryActionType { FREEZE = "FREEZE", SEIZE = "SEIZE", CONFISCATE = "CONFISCATE", LIQUIDATE = "LIQUIDATE", RESTRICT = "RESTRICT", RECOVER = "RECOVER" } export interface RegulatoryAuthority { authorityId: string; jurisdiction: string; // ISO 3166-1 alpha-2 country code authorityType: AuthorityType; publicKey: string; // For signature verification } export enum AuthorityType { NATIONAL_REGULATOR = "NATIONAL_REGULATOR", INTERNATIONAL_BODY = "INTERNATIONAL_BODY", INDUSTRY_REGULATOR = "INDUSTRY_REGULATOR", COURT_ORDER = "COURT_ORDER" } // Complex execution parameters for different action types export interface ActionParameters { immediateExecution: boolean; scheduledExecution?: number; // Unix timestamp conditionalExecution?: ConditionalTrigger; crossChainScope?: string[]; // Chain IDs to apply action exemptions?: string[]; // OCIDs exempt from action notificationRequirements?: NotificationConfig; }
Important Information: Cross-chain execution of regulatory actions is currently under development, with initial implementations supporting single-chain execution only.
The most difficult challenge in the actual implementation was handling Authority Hierarchy. For example, when a FREEZE action from the US SEC conflicts with a SEIZE action from international FATF, we needed to define at the protocol level which takes precedence.
// Authority hierarchy resolution impl RegulatoryAuthority { pub fn get_priority_level(&self) -> u8 { match self.authority_type { AuthorityType::CourtOrder => 10, // Highest priority AuthorityType::NationalRegulator => 8, AuthorityType::InternationalBody => 6, AuthorityType::IndustryRegulator => 4, } } pub fn can_override(&self, other: &RegulatoryAuthority) -> bool { if self.jurisdiction == other.jurisdiction { // Same jurisdiction: higher type wins self.get_priority_level() > other.get_priority_level() } else { // Cross-jurisdiction: only international bodies and court orders matches!(self.authority_type, AuthorityType::CourtOrder | AuthorityType::InternationalBody) } } }
OCID System: Technical Implementation of Identity Integration
The Oracle Contract ID (OCID) system is at the heart of the OIP message format. The implementation of this system, which unifies on-chain addresses, off-chain DAML Party IDs, and zero-knowledge proof-based identities, went through countless iterations and refinements.
Initially, we thought it would be as simple as linking three IDs together. In reality, we needed a complex identity mapping system for System-wide AML (Anti-Money Laundering).
// OCID Identity Resolution System export class OCIDResolver { private identityGraph: Map; private crossChainMappings: Map; constructor() { this.identityGraph = new Map(); this.crossChainMappings = new Map(); } async resolveIdentity(ocid: OCID): Promise { const unifiedId = this.computeUnifiedId(ocid); // Check existing identity graph let identityNode = this.identityGraph.get(unifiedId); if (!identityNode) { // Create new identity node identityNode = await this.createIdentityNode(ocid); this.identityGraph.set(unifiedId, identityNode); } return { ocid, unifiedId, crossChainAddresses: await this.resolveCrossChainAddresses(ocid), complianceStatus: await this.checkComplianceStatus(identityNode), riskScore: await this.calculateRiskScore(identityNode) }; } private computeUnifiedId(ocid: OCID): string { const combined = `${ocid.chainId}:${ocid.damlPartyId}:${ocid.zkId}`; return sha256(combined).toString('hex'); } private async resolveCrossChainAddresses(ocid: OCID): Promise { // Resolve addresses across different chains for the same identity const addresses: ChainAddress[] = []; // Query other chains for same zkId const relatedChains = this.crossChainMappings.get(ocid.zkId) || []; for (const chainId of relatedChains) { if (chainId !== ocid.chainId) { const address = await this.queryChainAddress(chainId, ocid.zkId); if (address) { addresses.push({ chainId, address }); } } } return addresses; } }
Message Routing and State Synchronization Optimization
To efficiently process large volumes of state synchronization messages, we designed a message routing system. This system routes messages to appropriate processing nodes based on message type and OCID.
// Message routing implementation type MessageRouter struct { processors map[MessageType][]MessageProcessor loadBalancer LoadBalancer metrics *RouterMetrics } func (r *MessageRouter) RouteMessage(msg *OIPMessage) error { // Validate message first if err := msg.Validate(); err != nil { r.metrics.IncreaseValidationErrors() return fmt.Errorf("message validation failed: %w", err) } // Get processors for message type processors, exists := r.processors[msg.MessageType] if !exists || len(processors) == 0 { return fmt.Errorf("no processors available for message type: %s", msg.MessageType) } // Load balance across available processors processor := r.loadBalancer.SelectProcessor(processors, msg) // Process with timeout and retry ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() return r.processWithRetry(ctx, processor, msg, 3) } func (r *MessageRouter) processWithRetry(ctx context.Context, processor MessageProcessor, msg *OIPMessage, maxRetries int) error { for attempt := 0; attempt < maxRetries; attempt++ { select { case <-ctx.Done(): return ctx.Err() default: } err := processor.ProcessMessage(msg) if err == nil { r.metrics.IncreaseSuccessfulProcessing() return nil } if !isRetryableError(err) { r.metrics.IncreaseProcessingErrors() return err } // Exponential backoff time.Sleep(time.Duration(1<
Development Challenges and Lessons Learned
The biggest realization while designing the OIP message format was that “complete state synchronization is not just about data format design—it’s about rearchitecting entire system architectures.”
Particularly, Atomic State Updates in cross-chain environments proved far more complex than anticipated. When a single regulatory action needs to be applied simultaneously across multiple chains, handling partial failure scenarios became a core challenge.
// Atomic cross-chain state update export class AtomicStateUpdater { async executeAtomicUpdate(updates: CrossChainUpdate[]): Promise { const transactionIds: string[] = []; const completedChains: string[] = []; try { // Phase 1: Prepare all chains for (const update of updates) { const txId = await this.prepareChainUpdate(update); transactionIds.push(txId); } // Phase 2: Commit all chains for (let i = 0; i < updates.length; i++) { await this.commitChainUpdate(updates[i], transactionIds[i]); completedChains.push(updates[i].chainId); } return { success: true, completedChains }; } catch (error) { // Rollback all completed chains await this.rollbackUpdates(completedChains, transactionIds); return { success: false, error: error.message }; } } }
Next Steps: The Journey Toward OIP v0.2
The OIP v0.1 message format is just the beginning. Through testnet deployment and testing real state synchronization scenarios, we’ll continue optimizing and refining the specification.
Key improvements for the next version will focus on message processing performance in high-frequency trading environments and consistency guarantees in large-scale cross-chain environments.
Tip: We are preparing an interactive schema validator and code examples for testing the OIP message format at docs.oraclizer.io Please stay tuned for documentation updates.
References:
[1]. JSON Schema Organization. (2024). JSON Schema Specification Draft 7. https://json-schema.org/specification-links.html
[2]. FATF. (2021). Updated Guidance for a Risk-Based Approach for Virtual Assets and Virtual Asset Service Providers. https://www.fatf-gafi.org/publications/fatfrecommendations/documents/guidance-rba-virtual-assets-2021.html
[3]. IEEE Standards Association. (2008). IEEE Standard for Precision Time Protocol for Networked Measurement and Control Systems. https://standards.ieee.org/ieee/1588/4355/
[4]. Message Passing Interface Forum. (2021). MPI: A Message-Passing Interface Standard Version 4.0. https://www.mpi-forum.org/docs/mpi-4.0/mpi40-report.pdf
[5]. XMPP Standards Foundation. (2023). XEP-0490: Message Displayed Synchronization. https://xmpp.org/extensions/xep-0490.html