Полная спецификация — docs/spec.md в proxy-shopping-go. На этой странице кратко изложено главное.

Ключи

Все ключи выводятся из одной мнемоники (BIP39), по одному на каждое назначение (все secp256k1).

Назначение Путь
идентичность (Nostr) m/44'/1237'/0'/0/0 (NIP-06)
libp2p m/7333'/0'/0'
BTC-ключ заказа (пользователь (user), покупатель-посредник (shopper)) m/7333'/1'/{idx}' (idx берётся из хеша ID заказа)
BTC-ключ заказа (гарант (escrow)) m/7333'/2'/{idx}. xpub публичен, поэтому другие могут вывести ключ, пока гарант офлайн
BTC-кошелёк m/84'/1'/0'/0/0
EVM m/44'/60'/0'/0/0

Доверие

kind Кто подписывает Содержимое
30500 координатор (coordinator) делегирование оператору; отзывается с revoked
30501 оператор (operator) список «регион × покупатель-посредник × гарант», рекомендуемые реле и точки доступа к блокчейнам
30502 / 30503 покупатель-посредник / гарант профиль (комиссии, регионы для наличных, xpub, адреса для выплат)
10050 все реле, на которых участник получает сообщения

Какое событие новее, решает версия в теге v (обычно время UNIX в момент подписи). Ничто не истекает. События передаются и через Nostr-реле, и через libp2p gossipsub; Go-узлы пересылают полученное по одному каналу в другой.

Сообщения

Сообщения оборачиваются по NIP-59 (gift wrap → seal → содержимое). Содержимое — это подписанное событие (kind 5400), чтобы гарант мог проверить его как третья сторона в споре.

  • Сообщение отправляется как минимум на k (по умолчанию 2) входящих реле получателя.
  • Получатель отвечает ack; отправитель повторяет отправку, пока не получит его.
  • Сообщение занимает не более 28000 байт. Крупные доказательства, например скриншоты, передаются только по хешу; в споре они отправляются гаранту по частям сообщениями attachment.
order.request + order.escrow_key → order.quote → order.accept → (funding) → order.funded + escrow.notice
→ order.purchased → order.shipping → order.release → order.completed
cancel and refund: order.cancel (before funding), order.refund (cooperative refund from the shopper)
other: ack (receipt), chat
dispute: dispute.open → dispute.evidence_request → dispute.evidence (+ attachment) → dispute.ruling → dispute.countersigned
report: report (to the operator)

BTC (P2WSH)

OP_IF
  OP_2 <user> <shopper> <escrow> OP_3 OP_CHECKMULTISIG
OP_ELSE
  OP_IF   <T1> OP_CHECKLOCKTIMEVERIFY OP_DROP <shopper> OP_CHECKSIG
  OP_ELSE <T2> OP_CHECKLOCKTIMEVERIFY OP_DROP <user> OP_CHECKSIG
  OP_ENDIF
OP_ENDIF

T1 < T2 (высоты блоков). Если пользователь так и не подтвердил получение и прошёл T1, покупатель-посредник может забрать средства в одиночку. Если покупатель-посредник исчез, пользователь после T2 может в одиночку вернуть их себе. Гарант должен вынести решение по спору до T1.

USDC (Safe v1.4.1)

  • Для каждого заказа создаётся Safe с тремя владельцами и порогом в две подписи.
  • Его адрес получается через CREATE2, поэтому его можно заранее вычислить по ID заказа.
  • Таймлоки реализованы как модуль Safe (PSEscrowModule).
    • После t1 покупатель-посредник может забрать средства через claimByShopper.
    • После t2 пользователь может вернуть их через refundToUser.
  • Выплаты и решения — это SafeTx, подписанные по EIP-712. Раздел средств по решению использует MultiSendCallOnly.

Привязка идентичности к ключам

Каждый запрос заказа (order.request) содержит key_proof: подпись ключом блокчейна, который входит в мультиподпись (BTC-ключ заказа или EVM-аккаунт), показывающую, что он принадлежит тому же человеку, что и идентичность Nostr. Без неё другая идентичность могла бы скопировать открытые ключи пользователя в свой запрос и заявить гаранту, что она и есть пользователь.

Адрес доставки

Адрес шифруется одноразовым ключом K (XChaCha20-Poly1305).

  • K оборачивается по NIP-44 для покупателя-посредника и отдельно для гаранта.
  • Копия для гаранта не входит в запрос: она передаётся покупателю-посреднику в order.escrow_key (в запросе есть только её хеш). В споре покупатель-посредник (или пользователь) передаёт её гаранту, поэтому гарант не может прочитать адрес в заказах без спора.

Регионы

Коды сопоставляются по префиксу: JP > JP-13 (Токио) > JP-13-13104 (Синдзюку). Уровень муниципалитетов использует японские коды органов местного самоуправления.