抱着一枚 Monero 币的 DUCAT 招财猫
面对面的点对点交易 · 规范草案 1.1.0-dev13

DUCAT

Veilid 负责传输。Monero 负责结算。 中间没有任何运营方。

任何地方都没有 DUCAT 服务器——联系人、消息、账单和收据都是 DHT 记录与端到端封装的载荷,而每一部手机 都跑着一个完整的 Veilid 节点,把自己用掉的中继和存储如数还回去。

0 条一致性向量 0 种运行模式 0 种语言 0 台服务器

调试签名,仅限 stagenet。暂时还不能用于真钱。

站在这些之上

中间没有任何人的交易

杜卡特曾是一枚金币,六个世纪里从威尼斯到维也纳再到黎凡特都通行无阻——与发行方没有关系,背后没有账户, 也不附带任何许可。DUCAT 就是这个东西,为你站在某个人面前谈生意的那一刻而做。

🐱

任何地方都没有服务器

联系人、账单、付款和收据都以端到端封装的载荷在 Veilid 的 DHT 上流动。没有东西可以 传唤、限速、下架或关停,因为压根没有东西在运行。每一部手机都是完整节点,把自己用掉的中继和存储 如数还回去。

🔑

身份就是一对密钥

在手机上生成。不要邮箱,不要手机号,没有谁可以去注册——这也正是没人能替你找回来的原因, 而应用把这句话直接说出来,而不是埋起来。

🎫

只能被领取一次的名片

你把一张名片递给某个人——二维码、ducat: 链接,或者一次 NFC 轻触——它只对 一个人有效。一旦被领取就失效,所以流到别处的截图并不是一条入口。

🔒

被封住的对话

形制取自 X3DH 的加密,配一次性预共享密钥与前向保密。当应用不得不退回到已签名的预共享密钥时,它会 给你看一把打开的锁,而不是把这件事藏起来。

💬

能带着钱走的对话

基于信箱的会话,即使有一方离线也照常运转——它承载逐项列明的账单,各行必须加得出总额, 否则消息会被拒收;还有付款,以及由收款方开出、指向他们所确认的那笔交易的收据。小群组也只是散入 同样的一对一会话:没有群密钥,没有共享记录,会话的每一项性质都原封不动。

六步,不需要账户,
大约一分钟

这个页面上的每一张截图,都是真机上运行的应用,不是效果图。这里没有任何东西要你注册,因为压根没有谁 可以去注册。

第 1 步,共 6 步:创建你的身份——在这部手机上生成的一对密钥,没有账户
1 · 身份——一对密钥,不是注册
第 4 步,共 6 步:设置 PIN,并把无法重置这件事明明白白写出来
4 · 一个 PIN——而且没有重置的办法,说得明明白白
第 5 步,共 6 步:陌生人之间如何互信——双方都押上押金,做完就还回来
5 · 信任——在你第一笔交易之前就讲清楚
第 6 步,共 6 步:备份——说老实话,在它离开这部手机之前还算不上备份
6 · 备份——在离开这部手机之前,「还算不上备份」

你的身份是一对在设备上生成的密钥——不要邮箱,不要手机号,没有谁可以去注册,这也正是没人能替你 找回来的原因。最后一步是备份,因为一个你恢复不了的钱包,才是在这里真正会让人丢钱的唯一途径。

真正属于你的钱

一个由你自己保管密钥的 Monero 钱包,各处都带法币折算,由内嵌的 monero-oxide 引擎完成扫描与支出—— 手机上没有任何钱包守护进程。

个人主页:以 XMR 和本地货币显示的余额、同步状态、可花票据计数、模式磁贴,以及最近的资金往来
余额,以及它背后的那些票据
付款或收款:以本地货币或 XMR 计的金额、手续费算式、付完之后还剩多少
全部算式,在你点头之前
流水:滚动余额、收据搜索、导出 CSV
从链上重建出来的一份流水
挑选付款对象:列出联系人及其密钥指纹
你在付给谁,看密钥指纹

付款时会显示金额、预估的网络手续费、合计、付完之后你还剩多少、要花掉几张票据,以及 大概需要多久——还让你自己挑速度。历史记录从链上重建:发出的款项用 key image 辨认,找零从不显示成收入, 每一行都带着滚动余额,整份流水都能导出为 CSV。

Monero 花的是一张张分开的票据,而不是一个余额,所以只揣着一张大额票据的钱包,可能连着付两次都付不了。 应用会数清能花的票据张数,让你在站到柜台前之前就先破开一张,而不是到了 那儿才卡住。

能带着钱走的对话

联系人是一对密钥,不是一个标识符。扫一次就打开一段基于信箱的对话,即使有一方离线也照常运转——而那些 会话承载的是交易,不只是文字。

你的名片:一张只能领一次、扫了就打开对话的二维码,只对一个人有效
你的名片——只对一个人有效
一段带着钱的对话:含税额行的逐项账单、收据,以及托管已结清的横幅
账单、付款、收据——都在会话里
共享板面上的三人群组:每一代一条记录,一个问题和它来自另一部手机的回答
群组——每一代一条共享记录
搜索人们说过的话,姓名命中排在消息命中之前
搜索人们实际说过的话

你把一张名片递给某个人——用二维码、ducat: 链接,或者一次 NFC 轻触——它 只对一个人有效:一旦被领取就失效,所以流到别处的截图并不是一条入口。

群组是一份只增不减的名单,它的话语搭在每一代一条共享的板面记录上(§16.24):每个成员在名单所携带的群密钥之下写自己的页,所以发给五个人的一条消息只是一次写入而不是五次,而网络也无法把那条记录同别的任何一条区分开来。没有人能冒充别人,退出则是本机自己的事。群组还能分摊账单:一个总额变成发给每个人的单独请求,外加一句让所有人都能核对算术的话,而每一笔请求都会在它的凭据抵达时翻成已付。

你的手机就是整门生意

从九种模式里挑一种,整个应用就把位置让出来——换上自己的标签页,钱包的痕迹一点不剩。这里头几乎没有 哪一项需要新的协议对象,而这正是规范最引以为傲的一句话。

运行模式:个人、收银台、酒水挂账、出租车、捐赠、集市、出租、找人帮忙、自助台
九种模式,一套协议
收银台:录入的商品,以及算好的销售税行
收银台——税额已经算好
给顾客扫的一个码;逐项账单落到他的手机上
一个码——顾客一扫,账单就到
酒水挂账:还开着的、已出账等着付的、用一张账单收尾的
酒水挂账——回家的公交上结清
自助台模式,顾客这一侧:想要什么就点什么,确认之前就把税显示出来
自助台——顾客自己点单
PIN 后面的自助台店员面板:订单、菜单,以及税率
店员面板——藏在 PIN 后面
一个常设的捐赠码,可关联性的代价写在屏幕上
捐赠——代价明明白白写着

收银台可以逐项录入,也可以接受手输的总额,按所在地区算好销售税,然后显示一个码; 顾客一扫,账单就落到他的手机上,付完之后收据再送回来。酒水挂账每上一杯就通知顾客 一次,最后用一张账单收尾。自助台把手机整个交给顾客,店员面板留在 PIN 后面。 捐赠箱把两条路都留着:一个 DUCAT 码,扫了就打开对话,钱一到便把收据送回;以及一个常设地址,任何 Monero 钱包都能往里打钱——而它在可关联性上的代价,写在屏幕上,不是写在条款里。

没有人派单的一趟车

输入你要去哪儿。手机把一个 GPS 位置变成一个地理格——一块以这个地方本身为地址的公共 布告栏——然后贴出一条召车:一张只能领一次的名片、一个大致的区域,以及按真实驾驶路线算出的报价。 布告栏上不会出现名字——名片上没有,名片背后的那条记录里也没有。司机盯着自己的 格子和相邻的格子,谁看到谁接;裁判这场竞速的是 DHT,而撮合方根本不存在。

绿色那条线才是重点:DUCAT 的价格比平台向乘客收取的低大约 15%,而且司机拿走 全部——那笔本会被抽走的平台佣金,还给了双方。接单信息是带着一张脸过来的:姓名、车型、 颜色、车牌、预计到达时间——这些只封给乘客一个人看,绝不会出现在布告栏上。同一时刻,你自己的 名字和你正站着的那个门口,反方向送出去,只送给接下这一单的那位司机。

只有一处让步,明明白白写在屏幕上:地址搜索、路线规划和地图瓦片会去问 OpenStreetMap——这是 DUCAT 唯一一处把位置发出设备的地方。布告栏本身承载的精度,从来不会细于约 1.2 公里。

召一趟车:路线、距离、时间,以及与平台会收多少并排着看的价格
召车,与平台价格并排着看
计价器:费率只输一次、一个上车码,开始时条款落进聊天里
计价器,条款就在会话里

把它卖掉、租出去,或者拿出你的一小时

同一套布告栏机制承载的远不止出行。一条集市信息、一次出租,或者一小时 有手艺的帮忙,都贴到以街区为地址的布告栏上,附近某个陌生人就能读到——而你们两个 谁也没碰过任何平台。

信息落在比召车更粗的布告栏上——横跨大约五公里,因为为了一串钥匙人们是愿意跑一趟的。搜索会读你的 布告栏和它外面那一圈,谁答上来就先画谁,所以停在你附近的东西不到六秒就出现在 屏幕上,而「这一带没有东西」要花四十秒左右,并且一路把查过的区域逐个划掉。

浏览集市:在布告栏上找到的一条信息,带价格、押金和一个问一问的按钮
集市
浏览帮忙:附近有人提供的一项手艺,按小时计价
找人帮忙
把车挂出去出租:会上布告栏的那一半公开内容
公开的那一半——布告栏能读到的部分
只给下单的人:地址和交接细节从不上布告栏
私密的那一半——绝不上布告栏
找附近的装备:一条正在挂着的信息,带日租价和押金
找到它——价格、押金,以及一条问一问的路

发布表单被切成两半,应用会告诉你哪一半是哪一半:第一部分里的一切都会上一块附近任何人都能读的 公共布告栏,而地址、钥匙放在哪儿、车牌号则进入第二部分——在预订真正成立之前,那部分不会离开 你的手机。

一个家、一条动态,
和一格自己的书架

能驮着账单走的记录,驮一页文字同样称职。你的密钥本来就是一个地址, 所以你写下的东西自有安身之处,不必向谁讨地方。

动态:你留着的那些人发的帖,以及你自己的家
动态——你留着的那些人发的帖
书架:按出版方归档的各期,是存出来而不是打开来读
书架——按出版方归档的各期
全球:按主题板面上的出版物,「全部」把每一格书架一并读完
全球——按主题的板面,所有书架一并读
打开的一条信息:它包裹里的照片、卖家的描述,以及敲下的价钱和旁边今天的折算
打开的一条信息——它的照片,和卖家敲下的价

一个身份的家就落在它自己的密钥所命名的地址上,而动态 是你留着的那些人的时间线——一条帖子可以带上一张图和一个文件,心也会回来。站点由发布它的人从自己的 磁盘上供出,并在一间封住的房间里打开,所以读一个站点不会从别处取来任何东西。

出版物——一份报纸、一部连载、一件软件——不按街区,而是在按主题的板面上 卖向全球:一条帖子既落在主题的板面上,也落在它语言的板面上,而「全部」把六格书架 并排读完。一条信息会从卖家自己的手机上取来它的包裹——最多 24 张原尺寸照片、一段 描述、规格和附件——遇到任何它没有指名的密钥就整包拒收,好让一份糟糕的文档藏不住好照片。

两边都押上一笔押金。
做完,它就回来了。

中间没有一家公司可以偏袒谁,所以 DUCAT 做了件更简单的事。钱停在一个只有你们两个人能打开的地址里, 解锁时按名字把各自那份押金还回去。没有人能把押金拿走——包括我们,因为压根不存在一个需要付钱的「我们」。

约 10% 一趟车 时间短,出损的风险小
约 20% 一处住所 别人家的钥匙
约 30% 一辆车 损失可能超过租金

那些数字是论证出来的,不是猜的——Bisq 那套不设托管人的 2-of-2 模型是最接近的、真在跑的先例,而关于 双向保证金的文献证明了这种安排在均衡状态下防作弊。由此得出两条边界:一笔押金若价值低于退还它的手续费, 它就变成零,而不是摆设;并且任何一笔都不超过价格的一半。风险敞口更大的一方后打款,这样他的钱就永远 不会独自躺在共享地址里。

⅔

背后没有公司的托管

当一笔交易需要的不止是一笔保证金:乘客、司机,以及一位双方都信任的仲裁联系人,在封住的会话上跑一次 分布式密钥生成——三台设备推导出同一个 Monero 地址,各自只握一份,任意两份即可 签名,任何地方都没有发牌人。顺利的路径就是两次点击。

其余一切都是协商:一方提出分法,另一方签字或者还价。被卡住的一方去找仲裁人,而仲裁人的联合签名 就是裁决——一个被拿下的仲裁人,最多只能在写明的当事人之间做选择,绝不可能付给 自己。在 stagenet 上以各种形态完成了链上验证,包括一次双输入的 FROST 解锁。

∅

一个名字付出过什么代价

在任何交易发生之前,先出现的是一个陌生人,而问题是:他的名字让他付出了什么代价。一个身份可以 销毁 Monero——把它发到一个谁都没有钥匙的地址,而这个地址是从任何人都能自己 重算一遍的哈希推出来的——并且带上 Monero 自己的付款证明。你的应用拿这份证明去问自己的节点, 再问第二个节点这笔交易有没有进区块,从此在你需要做决定的任何地方,都用话把这个身份讲清楚: 已销毁 0.01 XMR,自区块 N 起。从来不是一个分数。真正管用的规则是那句警告:当你即将 押上的东西比对方销毁的还值钱,应用就会这么说,因为一个不花代价的名字,消失时也没有什么可失去。

一笔交易了结之后,任何一方都可以签一份带评价的收据,而任何与你当面见过的人都可以 签一份担保。两者都走在封住的会话里,两者也都只由读的那一方来掂量——每个签名者 一票,而且只算你核验过其销毁的签名者,担保则只算来自已经在你的联系人里的人。只走一跳, 没有图谱,任何地方都不公开。这是一份花钱而不是花隐私换来的声誉。

而且它用起来像个正经应用

一个用着别扭的隐私工具,只会被用一次。这一个会说清每件事让你付出什么代价,会说二十种语言,还有一套 不是事后补上的深色主题。

个人资料:让联系人直接付款给你在可关联性上的代价,就写在你做这个选择的地方
代价写明,就在你做选择的那一刻
同一个主页界面,换成深色 Mocha 主题
Mocha——深色主题,认真做的
同一个主页界面换成阿拉伯语:完整的从右到左镜像,配东阿拉伯数字
阿拉伯语——完整的从右到左镜像
设置里做生意的那一半:计价地区、销售税率、隐私、备份
设置里做生意的那一半

在一套隐私系统里,方便是有价钱的,而应用把这个价钱写在你做选择的那一刻,不是写在没人读的条款里。让 联系人直接付款给你,意味着重复使用同一个地址——所以它会说,任何盯着链看的人都能看出每次收钱的是同 一个人,连那些只付过你一次的人也能看出来,然后指出替代做法。同样的习惯贯穿始终: 在内存池里看到的东西,显示为看到了,但从未结算;个人资料永远按它本来的样子呈现——一份 自述。

它会说二十种语言——每个界面都从资源文件读字,复数是组装出来的而不是拼接出来的,而且阿拉伯语 和波斯语证明了那套镜像是真的。收据是记录,不是消息:每一张收据住在自己的库里,会话和联系人 被删掉之后依然留着,并且随加密备份一起走——那份备份带走的不只是钱,还有人。

同一个应用,摆到桌面上

架在手机所跑的同一份 Rust 之上的原生窗口——于是小店主的收银台、自助台、 新闻室、集市和动态,是同一套实现,不是移植。

桌面端的聊天页:一对一会话之上、板面上的群组,以及一块对话面板
聊天——一对一会话之上,板面上的群组
桌面端的收银台:录好的一笔买卖,和酒水挂账
收银台——录好的一笔买卖,和酒水挂账
桌面端的集市页:某个格子附近的信息,连着它们的缩略图
集市——某个格子附近的信息,连着缩略图
桌面端的状态页:节点、它的路由表、重新连接,以及日志
状态——节点、它的路由表,以及重新连接

它从同一批资源里说手机的二十种语言,并且在每一道仪式上都对着手机验证过:聊天、板面上的群组、 账单与收据、由一部从没见过这张桌子的手机付掉的自助台订单、供出与取回的文件和站点,还有带真实声音的通话。

什么已经验证过,什么还没有

README 记着一本账:在真实网络和真实 stagenet 上,究竟有哪些流程真的从头跑到了尾——并且把缺口写出来, 而不是埋起来。

从头到尾演示过
  • 两趟完整的派单出行手机↔桌面端——从地理格召车到链上结算,含小费与收据——而押过金的那趟,如今也已在两部手机之间跑到了终点
  • 托管的完整闭环——两部手机加一位坐在桌前的仲裁人,各自独立推导出同一个 2-of-3 地址;打款 → 解锁 → 司机收款
  • 一次预订——房东与住客各自打入押金,资金池由住客自己扫码确认,提出分法、签字,双方都毫发无损;中途有个 Monero 节点超时,它是接着原来的托管往下走,而不是从头再来一遍
  • 酒水挂账从手机到桌面端,从账单到收据;十种攻击被拒
  • 一部擦得干干净净的手机被从头走了一遍设置:第一张名片、第一笔付款、一单从头付到尾的自助台买卖——然后再擦一次,用它自己的备份还原回来
  • 所有托管路径在同一天里全部走完界面——一笔集市买卖、一次街边召车(实时位置在两部手机之间来回)、一次器材出租,以及一单帮忙的活儿——后者头一回跑就把一个真实的竞态暴露出来并当场修好
  • 信任这一层,是走过的,不是讲出来的——一份销毁被证明,并在 12 秒内由陌生人自己的节点核验过;一份带评价的收据被正确地掂量为零;四台桌面端得出「你的 2 位联系人认识对方」;还有一张没有名字的召车名片,名字是随后分享的资料给的
写出来了,没有埋起来
  • 还没有项目之外的任何人做过审查——§2.5 正是说明这为何重要的论证;内部对五个受攻击面走了一遍,找出四十来处并修好了其中大半,那是一件分量更轻的事
  • 还没有哪个实现者是从没读过 core/ 的
  • NFC 只在编译期验证过,从未在两部手机之间做过实地测试——那一下轻触也从没在真机上量过
  • 延迟数字是在直连节点的桌面机上测的,不是在靠电池跑的手机上
  • 两套无线电、两块电池、两只各自独立的时钟——那一天的实地测试还欠着
  • 桌面端没有第二因素——既没有设备锁,也没有自己的秘密,所以手机在钱出去之前设的那道关卡,在那里跑不起来
!

预 Alpha。Stagenet。不能用于真钱。

每一个构建都带调试签名,在 Monero 的 stagenet 上结算——按设计就是没有价值的币。规范是 草案 1.1.0-dev13,它这样评价自己:这里没有任何东西是定稿,§14 才是真正的议程。拿去试、 拿去砸、拿去读——只是别往里投钱。

规范就是产品。
其余一切都在把它按在诚实上。

ducat-protocol.md 是首要产物——五个部分,变更记录放在最前面,还有一套由第二个实现来跑的 一致性测试,好让文档没法悄悄地和代码走散。

★ ducat-protocol.md   规范文档——草案 1.1.0-dev13,变更记录在最前
  core/               参考实现(Rust)
  vectors/            406 条一致性用例 + 模式定义
  conformance/        四个检查器,每次 push 都跑
  harness/            在真实的 Veilid 线路上从头跑到尾
  sim/                离线模拟器,市场情景
  applications/       android/ + desktop/(同一份源码)
  mobile/             Rust 桥接层:钱包 · 信箱 · 节点
  research/           证据,不是产品

四个检查器,每次 push 都跑

  • 每一条向量都对着 schema.json 校验
  • 第二个实现把 406 条全跑一遍,且必须一致
  • 拿文档对着代码审计——正是这样揪出了一个被引用三次却从没写过的规范性章节、一个注册表根本没有声明的取值范围,以及六种文档从未提过名字的向量类型
  • 凡是引进来的,没有一样是未声明的

守护的责任是规范性的,不是许愿:§18.7 把 Veilid 的伦理变成一致性要求——没有协议费,不给节点 付钱,永远如此。拿这份代码去造什么都行,但一个靠承载牟利的客户端不是 DUCAT。

装到手机上,
或者摆到桌面上

不用先装别的——每个构建都自带 Rust 库和 JVM。手机上是 Android,而在 Linux、 Windows 和 macOS 上,是同一个应用以原生窗口的样子立着。没有 iOS 版;等它配得上的那天, 再给它一个文件夹。

🤖 Android

手机浏览器,最新构建,不用去翻发布页。armeabi-v7a 只给大约 2016 年以前的手机; x86_64 是给模拟器的。

🐧 Linux

.deb 和 .rpm 按包安装;AppImage 直接就能跑。

🪟 Windows

未签名——首次打开时 Windows 会弹警告。

🍎 macOS

Apple Silicon 与 Intel 两种构建。未签名——首次打开时 macOS 会弹警告。

调试签名,仅限 stagenet——不能用于真钱。