Приложения

Эксплорер TimeChain

Откройте любой перевод монет Montana по его идентификатору или ссылке подтверждения и проверьте его сами. Запись читается с узлов; её хеш, постквантовая подпись и баланс её ключа — свёрнутый из всей цепи ключа — проверяются здесь, в вашем браузере.

Контрольная точка сети

Последние переводы

Новейшие записи на каждом узле. Каждая строка проверяется здесь, как только показана: её хеш, подпись и место в цепи её ключа.

Как проверить самостоятельно

Перевод — одна запись, подписанная сидом отправителя и положенная в цепь его ключа на узле, который выбирает ключ; журнал только дописывается: запись никогда не заменяется и не удаляется. Целые числа записаны от младшего байта (little-endian).

Ниже — раскладка записи, её подпись, идентификатор и адрес отправителя в записи Python (срез [a:b] берёт байты с a по b − 1).

Запись — 5443 байт

Запись несёт свой вид и поля, которыми этот вид пользуется; поле, которым вид не пользуется, нулевое. SEND: pair — метка пары, to = SHA-256(mt-coin-to ‖ from получателя ‖ blind), где blind = HMAC-SHA-256 под секретом пары над mt-coin-blind ‖ nonce, а t0 — часть суммы, объявленная, а не отчеканенная. RECV: to несёт blind, ref — принимаемый SEND, t0 — ту же объявленную часть. REFUND: ref называет собственный SEND отправителя. MINT: t0 и t1 ограничивают отчеканенные секунды. GENESIS: баланс, который кошелёк ключа объявил при открытии цепи.

СмещениеБайтПолеСодержимое
04magicASCII MTTX
41version0x03
51kind1 SEND, 2 RECV, 3 MINT, 4 REFUND, 5 GENESIS
68amountu64, монеты, не меньше 1
148birthu64, миллисекунды с 1970-01-01 UTC, по часам отправителя
2216nonceслучайные байты
3832prevидентификатор предыдущей записи ключа в его цепи; 32 нулевых байта у первой
7032pairHMAC-SHA-256 на секрете пары от ASCII mt-coin-pair-v1
10232toSEND: обязательство получателя; RECV: blind, который его раскрывает
13432refRECV, REFUND: идентификатор SEND, судьбу которого они решают
1668t0MINT: первая отчеканенная секунда (секунды узла с 1970); SEND, RECV: объявленная часть суммы
1748t1MINT: секунда после последней отчеканенной
1821952pubkeyоткрытый ключ ML-DSA-65 отправителя
21343309sigподпись ML-DSA-65
msg   = b"mt-coin-tx-v3" + record[0:2134]
pk    = record[182:2134]
sig   = record[2134:5443]
valid = ML-DSA-65.Verify(pk, msg, sig, ctx=b"")   # FIPS 204
id    = SHA-256(record).hex()
from  = SHA-256(pk).hex()
prev  = record[38:70].hex()   # 64 zeros: the key's first record
chain = ["montana.quest", "montana.xxx"][SHA-256(pk)[0] % 2]   # the node whose word counts

Цепь ключа живёт на одном узле неизменной таблицы узлов цепей протокола, упорядоченной по имени (формула выше): на том, что стоит под номером from[0] mod n, где from[0] — первый байт адреса отправителя. Этот узел принимает запись только поверх головы ключа, поэтому из двух записей на одной предшественнице стоит только одна. Идя по prev от записи к записи, можно пройти цепь ключа назад до её первой записи; эта страница связывает каждую запись с предыдущей.

Баланс ключа — свёртка его цепи от первой записи: MINT прибавляет не больше 2 × min(13, max(1, bit_length(s) − 1)) монет за s-ю секунду от первой записи ключа, за отрезок не короче 60 секунд, начинающийся там, где кончился прошлый; SEND вычитает — сначала отчеканенное — и не может увести ниже нуля; RECV прибавляет SEND, чей to раскрывается на этот ключ его blind; REFUND возвращает собственный SEND ключа через сутки, если его никто не принял. SEND, положенный, пока ключ получателя ещё не был известен, получателя не называет: он вычитает так же, а кошелёк получателя вносит монеты в свою цепь чеканкой в пределах своего времени. Узел цепи SEND решает судьбу каждого SEND один раз: принят или возвращён, но не то и другое.

Подпись — ML-DSA-65 по FIPS 204 с пустым контекстом, поэтому её проверит любая реализация стандарта. Эта страница проверяет её ядром Montana, собранным в WebAssembly, — тем же модулем, что работает в веб-приложении Montana, — а SHA-256 считает Web Crypto вашего браузера.

Метку пары знают только два конца разговора: любой видит, что адрес заплатил паре, но не видит, кто эта пара.

Каждый узел проверяет подпись ядром Montana прежде, чем принять запись, и каждый читатель проверяет её снова прежде, чем поверить, — кошелёк перед зачислением, эта страница перед словом «верна». Прочитайте запись сами с любого узла; команда печатает длину записи, её идентификатор и адрес отправителя:

curl -s -H 'Content-Type: application/json' -d '{"id":"{id}"}' https://montana.xxx/pushwake/tx-get | python3 -c 'import sys,json,base64,hashlib; r=base64.b64decode(json.load(sys.stdin)["rec"]); k={1:(69,2021),2:(101,2053),3:(182,2134)}[r[4]]; print(len(r), hashlib.sha256(r).hexdigest(), hashlib.sha256(r[k[0]:k[1]]).hexdigest())'
curl -s -H 'Content-Type: application/json' -d '{"id":"{id}"}' https://montana.xxx/pushwake-msk/tx-get | python3 -c 'import sys,json,base64,hashlib; r=base64.b64decode(json.load(sys.stdin)["rec"]); k={1:(69,2021),2:(101,2053),3:(182,2134)}[r[4]]; print(len(r), hashlib.sha256(r).hexdigest(), hashlib.sha256(r[k[0]:k[1]]).hexdigest())'

Что доказывает запись. Каждое движение баланса ключа — запись его цепи: монеты входят только чеканкой MINT (не больше, чем позволяет прожитое ключом время), зачислением RECV (раскрытием перевода, адресованного этому ключу) или возвратом REFUND (перевода, который за сутки никто не принял), а выходят только переводом SEND. Узел цепи отказывает в SEND сверх того, что держит цепь, каждый узел и каждый телефон, хранящий копию, сворачивают ту же цепь, и эта страница сворачивает её снова в вашем браузере.

Ссылка подтверждения перевода — https://montana.xxx/explorer/#tx= и его идентификатор.

Формат: запись монет Montana (MTTX).