412 lines
14 KiB
Python
412 lines
14 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
sovanaglobus.py — Uber для океана (Sovanaglobus Protocol)
|
||
|
||
Книга Монтана, Глава 04:
|
||
> "Бот делает убер между всеми участниками цепочки фрахтования морских.
|
||
> (Грузовладельцев, Судовладельцев, Агентов и тд)"
|
||
> "Uber убрал диспетчера такси. #Сованаглобус убирает посредника в фрахте."
|
||
> "#Сованаглобус = протокол, не продукт.
|
||
> Как Bitcoin Core — бесплатный софт."
|
||
|
||
Sovanaglobus — это ПРОТОКОЛ, не платформа:
|
||
- P2P фрахтование без брокера
|
||
- Любые валюты (полная совместимость с фиатом)
|
||
- Общий язык измерения — время
|
||
"""
|
||
|
||
from dataclasses import dataclass, field
|
||
from datetime import datetime, timezone
|
||
from decimal import Decimal
|
||
from typing import Dict, List, Optional, Set
|
||
from enum import Enum
|
||
import hashlib
|
||
|
||
|
||
class ParticipantType(Enum):
|
||
"""Типы участников рынка фрахта."""
|
||
CARGO_OWNER = "cargo_owner" # Грузовладелец
|
||
SHIP_OWNER = "ship_owner" # Судовладелец
|
||
AGENT = "agent" # Агент
|
||
PORT = "port" # Порт
|
||
BROKER = "broker" # Брокер (устраняется)
|
||
|
||
|
||
class CargoType(Enum):
|
||
"""Типы грузов."""
|
||
BULK = "bulk" # Навалочный (зерно, уголь)
|
||
CONTAINER = "container" # Контейнерный
|
||
TANKER = "tanker" # Наливной (нефть, газ)
|
||
GENERAL = "general" # Генеральный
|
||
REEFER = "reefer" # Рефрижераторный
|
||
|
||
|
||
@dataclass
|
||
class Participant:
|
||
"""Участник протокола."""
|
||
node_id: str # Montana node ID
|
||
participant_type: ParticipantType
|
||
name: str
|
||
montana_address: str # mt... адрес
|
||
accepted_currencies: List[str] = field(default_factory=lambda: ["USD", "EUR", "Ɉ"])
|
||
|
||
def to_dict(self) -> dict:
|
||
return {
|
||
"node_id": self.node_id,
|
||
"type": self.participant_type.value,
|
||
"name": self.name,
|
||
"address": self.montana_address,
|
||
"currencies": self.accepted_currencies
|
||
}
|
||
|
||
|
||
@dataclass
|
||
class FreightRequest:
|
||
"""Запрос на фрахт (от грузовладельца)."""
|
||
request_id: str
|
||
cargo_owner: str # node_id грузовладельца
|
||
cargo_type: CargoType
|
||
origin_port: str
|
||
destination_port: str
|
||
volume: str # "50,000 MT" / "500 TEU"
|
||
laydays: str # Период погрузки
|
||
currency: str # Предпочтительная валюта
|
||
created_at: str
|
||
|
||
def to_hash(self) -> str:
|
||
data = f"{self.request_id}:{self.cargo_owner}:{self.origin_port}:{self.destination_port}"
|
||
return hashlib.sha256(data.encode()).hexdigest()
|
||
|
||
|
||
@dataclass
|
||
class FreightOffer:
|
||
"""Предложение фрахта (от судовладельца)."""
|
||
offer_id: str
|
||
request_id: str # К какому запросу
|
||
ship_owner: str # node_id судовладельца
|
||
vessel_name: str
|
||
vessel_imo: str
|
||
rate: Decimal # Ставка
|
||
currency: str # Валюта ставки
|
||
eta: str # Ожидаемое прибытие
|
||
created_at: str
|
||
|
||
def to_hash(self) -> str:
|
||
data = f"{self.offer_id}:{self.ship_owner}:{self.rate}:{self.currency}"
|
||
return hashlib.sha256(data.encode()).hexdigest()
|
||
|
||
|
||
@dataclass
|
||
class FreightContract:
|
||
"""Контракт фрахтования (P2P, без брокера)."""
|
||
contract_id: str
|
||
request_id: str
|
||
offer_id: str
|
||
cargo_owner: str
|
||
ship_owner: str
|
||
rate: Decimal
|
||
currency: str
|
||
time_value_juno: Decimal # Эквивалент в Ɉ
|
||
signed_at: str
|
||
status: str = "active"
|
||
|
||
def to_hash(self) -> str:
|
||
data = f"{self.contract_id}:{self.cargo_owner}:{self.ship_owner}:{self.signed_at}"
|
||
return hashlib.sha256(data.encode()).hexdigest()
|
||
|
||
|
||
class SovanaglobusProtocol:
|
||
"""
|
||
Протокол P2P фрахтования.
|
||
|
||
Книга Монтана:
|
||
> "Все пользуются какой хотят валютой, также как и у Убера.
|
||
> Полная привязка к фиатной системе через тех кто уже в Логистике."
|
||
> "Даём ей общий язык измерения."
|
||
"""
|
||
|
||
VERSION = "0.1.0"
|
||
|
||
# Рынок морского фрахта
|
||
MARKET_SIZE = "$14-16 trillion/year"
|
||
WORLD_TRADE_SHARE = "90% by volume"
|
||
|
||
def __init__(self):
|
||
self.participants: Dict[str, Participant] = {}
|
||
self.requests: Dict[str, FreightRequest] = {}
|
||
self.offers: Dict[str, FreightOffer] = {}
|
||
self.contracts: Dict[str, FreightContract] = {}
|
||
|
||
def register_participant(
|
||
self,
|
||
node_id: str,
|
||
participant_type: ParticipantType,
|
||
name: str,
|
||
montana_address: str,
|
||
currencies: List[str] = None
|
||
) -> Participant:
|
||
"""
|
||
Зарегистрировать участника в протоколе.
|
||
|
||
Args:
|
||
node_id: Montana node ID
|
||
participant_type: Тип участника
|
||
name: Название компании
|
||
montana_address: mt... адрес
|
||
currencies: Принимаемые валюты
|
||
|
||
Returns:
|
||
Participant
|
||
"""
|
||
participant = Participant(
|
||
node_id=node_id,
|
||
participant_type=participant_type,
|
||
name=name,
|
||
montana_address=montana_address,
|
||
accepted_currencies=currencies or ["USD", "EUR", "Ɉ"]
|
||
)
|
||
self.participants[node_id] = participant
|
||
return participant
|
||
|
||
def create_request(
|
||
self,
|
||
cargo_owner_id: str,
|
||
cargo_type: CargoType,
|
||
origin: str,
|
||
destination: str,
|
||
volume: str,
|
||
laydays: str,
|
||
currency: str = "USD"
|
||
) -> FreightRequest:
|
||
"""
|
||
Создать запрос на фрахт.
|
||
|
||
Args:
|
||
cargo_owner_id: node_id грузовладельца
|
||
cargo_type: Тип груза
|
||
origin: Порт погрузки
|
||
destination: Порт выгрузки
|
||
volume: Объём
|
||
laydays: Период погрузки
|
||
currency: Валюта
|
||
|
||
Returns:
|
||
FreightRequest
|
||
"""
|
||
if cargo_owner_id not in self.participants:
|
||
raise ValueError(f"Participant not registered: {cargo_owner_id}")
|
||
|
||
request_id = hashlib.sha256(
|
||
f"{cargo_owner_id}:{datetime.now().isoformat()}".encode()
|
||
).hexdigest()[:16]
|
||
|
||
request = FreightRequest(
|
||
request_id=request_id,
|
||
cargo_owner=cargo_owner_id,
|
||
cargo_type=cargo_type,
|
||
origin_port=origin,
|
||
destination_port=destination,
|
||
volume=volume,
|
||
laydays=laydays,
|
||
currency=currency,
|
||
created_at=datetime.now(timezone.utc).isoformat()
|
||
)
|
||
|
||
self.requests[request_id] = request
|
||
return request
|
||
|
||
def submit_offer(
|
||
self,
|
||
ship_owner_id: str,
|
||
request_id: str,
|
||
vessel_name: str,
|
||
vessel_imo: str,
|
||
rate: float,
|
||
currency: str,
|
||
eta: str
|
||
) -> FreightOffer:
|
||
"""
|
||
Подать предложение на запрос.
|
||
|
||
Args:
|
||
ship_owner_id: node_id судовладельца
|
||
request_id: ID запроса
|
||
vessel_name: Название судна
|
||
vessel_imo: IMO номер
|
||
rate: Ставка
|
||
currency: Валюта
|
||
eta: Ожидаемое прибытие
|
||
|
||
Returns:
|
||
FreightOffer
|
||
"""
|
||
if request_id not in self.requests:
|
||
raise ValueError(f"Request not found: {request_id}")
|
||
|
||
offer_id = hashlib.sha256(
|
||
f"{ship_owner_id}:{request_id}:{datetime.now().isoformat()}".encode()
|
||
).hexdigest()[:16]
|
||
|
||
offer = FreightOffer(
|
||
offer_id=offer_id,
|
||
request_id=request_id,
|
||
ship_owner=ship_owner_id,
|
||
vessel_name=vessel_name,
|
||
vessel_imo=vessel_imo,
|
||
rate=Decimal(str(rate)),
|
||
currency=currency,
|
||
eta=eta,
|
||
created_at=datetime.now(timezone.utc).isoformat()
|
||
)
|
||
|
||
self.offers[offer_id] = offer
|
||
return offer
|
||
|
||
def match_and_sign(
|
||
self,
|
||
request_id: str,
|
||
offer_id: str
|
||
) -> FreightContract:
|
||
"""
|
||
Сопоставить и подписать контракт (P2P, без брокера).
|
||
|
||
Args:
|
||
request_id: ID запроса
|
||
offer_id: ID предложения
|
||
|
||
Returns:
|
||
FreightContract
|
||
"""
|
||
request = self.requests.get(request_id)
|
||
offer = self.offers.get(offer_id)
|
||
|
||
if not request or not offer:
|
||
raise ValueError("Request or offer not found")
|
||
|
||
if offer.request_id != request_id:
|
||
raise ValueError("Offer does not match request")
|
||
|
||
contract_id = hashlib.sha256(
|
||
f"{request_id}:{offer_id}:{datetime.now().isoformat()}".encode()
|
||
).hexdigest()[:16]
|
||
|
||
# Конвертация в Ɉ (через Beeple benchmark)
|
||
# $0.16/sec = базовая ставка
|
||
# Примерная конверсия: rate / 0.16 * estimated_voyage_seconds
|
||
estimated_days = 14 # Типичный рейс
|
||
time_value = offer.rate / Decimal("0.16") * Decimal(estimated_days * 86400)
|
||
|
||
contract = FreightContract(
|
||
contract_id=contract_id,
|
||
request_id=request_id,
|
||
offer_id=offer_id,
|
||
cargo_owner=request.cargo_owner,
|
||
ship_owner=offer.ship_owner,
|
||
rate=offer.rate,
|
||
currency=offer.currency,
|
||
time_value_juno=time_value,
|
||
signed_at=datetime.now(timezone.utc).isoformat()
|
||
)
|
||
|
||
self.contracts[contract_id] = contract
|
||
return contract
|
||
|
||
def get_protocol_stats(self) -> Dict:
|
||
"""Статистика протокола."""
|
||
return {
|
||
"version": self.VERSION,
|
||
"market_size": self.MARKET_SIZE,
|
||
"world_trade_share": self.WORLD_TRADE_SHARE,
|
||
"participants": len(self.participants),
|
||
"active_requests": len(self.requests),
|
||
"active_offers": len(self.offers),
|
||
"signed_contracts": len(self.contracts),
|
||
"broker_eliminated": True,
|
||
"common_measure": "Time (Ɉ)"
|
||
}
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# DEMO
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
if __name__ == "__main__":
|
||
protocol = SovanaglobusProtocol()
|
||
|
||
print("=" * 60)
|
||
print("SOVANAGLOBUS — Uber для океана")
|
||
print("=" * 60)
|
||
print("\n'Протокол, не продукт. Как Bitcoin Core.'")
|
||
|
||
# Регистрация участников
|
||
print("\n--- РЕГИСТРАЦИЯ УЧАСТНИКОВ ---")
|
||
|
||
cargo_owner = protocol.register_participant(
|
||
node_id="node_cargo_001",
|
||
participant_type=ParticipantType.CARGO_OWNER,
|
||
name="Grain Trading LLC",
|
||
montana_address="mt1234567890abcdef1234567890abcdef12345678"
|
||
)
|
||
print(f"Грузовладелец: {cargo_owner.name}")
|
||
|
||
ship_owner = protocol.register_participant(
|
||
node_id="node_ship_001",
|
||
participant_type=ParticipantType.SHIP_OWNER,
|
||
name="Baltic Shipping Co",
|
||
montana_address="mtabcdef1234567890abcdef1234567890abcdef12"
|
||
)
|
||
print(f"Судовладелец: {ship_owner.name}")
|
||
|
||
# Запрос на фрахт
|
||
print("\n--- ЗАПРОС НА ФРАХТ ---")
|
||
|
||
request = protocol.create_request(
|
||
cargo_owner_id="node_cargo_001",
|
||
cargo_type=CargoType.BULK,
|
||
origin="Novorossiysk",
|
||
destination="Rotterdam",
|
||
volume="50,000 MT",
|
||
laydays="01-05 Feb 2026",
|
||
currency="USD"
|
||
)
|
||
print(f"ID: {request.request_id}")
|
||
print(f"Маршрут: {request.origin_port} → {request.destination_port}")
|
||
print(f"Груз: {request.cargo_type.value}, {request.volume}")
|
||
|
||
# Предложение от судовладельца
|
||
print("\n--- ПРЕДЛОЖЕНИЕ ---")
|
||
|
||
offer = protocol.submit_offer(
|
||
ship_owner_id="node_ship_001",
|
||
request_id=request.request_id,
|
||
vessel_name="MV BALTIC PRIDE",
|
||
vessel_imo="9876543",
|
||
rate=450000,
|
||
currency="USD",
|
||
eta="2026-02-01"
|
||
)
|
||
print(f"Судно: {offer.vessel_name}")
|
||
print(f"Ставка: ${offer.rate:,.0f}")
|
||
|
||
# P2P контракт (без брокера!)
|
||
print("\n--- P2P КОНТРАКТ (БЕЗ БРОКЕРА) ---")
|
||
|
||
contract = protocol.match_and_sign(request.request_id, offer.offer_id)
|
||
print(f"Contract ID: {contract.contract_id}")
|
||
print(f"Ставка: ${contract.rate:,.0f} ({contract.currency})")
|
||
print(f"Эквивалент в Ɉ: {contract.time_value_juno:,.0f}")
|
||
print(f"Брокер: НЕТ (P2P)")
|
||
|
||
# Статистика
|
||
print("\n--- СТАТИСТИКА ПРОТОКОЛА ---")
|
||
stats = protocol.get_protocol_stats()
|
||
print(f"Рынок: {stats['market_size']}")
|
||
print(f"Доля мировой торговли: {stats['world_trade_share']}")
|
||
print(f"Участников: {stats['participants']}")
|
||
print(f"Брокер устранён: {'ДА' if stats['broker_eliminated'] else 'НЕТ'}")
|
||
print(f"Общая мера: {stats['common_measure']}")
|
||
|
||
print("\n" + "=" * 60)
|
||
print("'Uber убрал диспетчера. Sovanaglobus убирает брокера.'")
|
||
print("=" * 60)
|