registrar: an older commit can still register a name inside its contest window
A front-runner's registration makes the name look taken, and the registrar refused to reveal - exactly when revealing is what wins the name back. The availability check now says whether the contest is open and what a challenger must beat, and a reveal whose commit is older goes ahead. Index entries record the commit a registration revealed, so that comparison can be made.
This commit is contained in:
parent
d19cd6bde7
commit
cc683130b5
3 changed files with 26 additions and 4 deletions
|
|
@ -296,6 +296,7 @@ export async function buildIndex(provider, { enforceTld = false, cosignGate = tr
|
|||
const entry = {
|
||||
name, category: mint.tokenData.category, records: payload.r ?? {},
|
||||
txid: tx_hash, height, updatedTxid: null,
|
||||
...(won.commit ? { commit: { height: won.commit.height, txid: won.commit.txid } } : {}),
|
||||
};
|
||||
names.set(name, entry);
|
||||
byCategory.set(entry.category, entry);
|
||||
|
|
|
|||
|
|
@ -109,11 +109,21 @@ export async function checkAvailability(client, name) {
|
|||
// (PROTOCOL-ADDENDUM-release.md, the one-block gap).
|
||||
const releasedAt = index.released?.get(key);
|
||||
const releasePending = releasedAt !== undefined && !(releasedAt > 0);
|
||||
// A registration can still be displaced, for contestBlocks after it is
|
||||
// mined, by one whose commit is older (PROTOCOL-ADDENDUM-commit-reveal.md).
|
||||
// `contest` says whether that window is open and what a challenger must beat.
|
||||
const contestBlocks = NETWORK.commitReveal?.contestBlocks ?? 0;
|
||||
const contest = entry ? {
|
||||
open: !(entry.height > 0) || (index.height ?? 0) + 1 <= entry.height + contestBlocks,
|
||||
finalAt: entry.height > 0 ? entry.height + contestBlocks + 1 : null,
|
||||
rank: entry.commit ? [entry.commit.height, entry.commit.txid] : [entry.height > 0 ? entry.height : Infinity, entry.txid],
|
||||
} : null;
|
||||
return {
|
||||
name: key,
|
||||
rawName: raw,
|
||||
available: !entry && !releasePending,
|
||||
entry,
|
||||
contest,
|
||||
releasedAt: releasedAt ?? null,
|
||||
releasePending,
|
||||
reason: entry ? "registered" : releasePending ? "release-pending" : null,
|
||||
|
|
@ -332,19 +342,27 @@ export async function registerWithBuiltInWallet(client, {
|
|||
commit = null,
|
||||
onProgress = () => {},
|
||||
}) {
|
||||
let committed = null;
|
||||
if (commit) {
|
||||
if (normalizeName(commit.name) !== normalizeName(name)) throw new Error("this commit was made for another name");
|
||||
if (commit.lock !== binToHex(addressToLockingBytecode(wallet.address))) throw new Error("this commit was made for another owner");
|
||||
const state = await commitState(client, commit);
|
||||
const state = committed = await commitState(client, commit);
|
||||
if (!state.mature) throw new Error(state.expired
|
||||
? "this commit is too old to reveal — commit again"
|
||||
: "this commit is not in a block yet — the registration can be sent after the next block");
|
||||
}
|
||||
onProgress("checking-availability");
|
||||
const availability = await checkAvailability(client, name);
|
||||
if (!availability.available) throw new Error(availability.releasePending
|
||||
? `"${availability.name}" was just released — it can be registered once that release is in a block`
|
||||
: `"${availability.name}" is already registered`);
|
||||
if (!availability.available) {
|
||||
// Somebody registered it first — perhaps by copying this very name out of
|
||||
// the mempool. An older commit still takes it while the contest is open.
|
||||
const c = availability.contest;
|
||||
const older = committed && c && (committed.height < c.rank[0] || (committed.height === c.rank[0] && commit.txid < c.rank[1]));
|
||||
if (!(c?.open && older)) throw new Error(availability.releasePending
|
||||
? `"${availability.name}" was just released — it can be registered once that release is in a block`
|
||||
: `"${availability.name}" is already registered`);
|
||||
onProgress("contesting", { incumbent: availability.entry.txid, finalAt: c.finalAt });
|
||||
}
|
||||
|
||||
onProgress("loading-coins");
|
||||
let utxos = await getUtxosForAddresses(client, wallet.watchedAddresses);
|
||||
|
|
|
|||
|
|
@ -811,6 +811,8 @@ export async function buildIndex({ network = NETWORK, electrum = getNetwork(netw
|
|||
names.set(key, {
|
||||
name: key, category: mint.tokenData.category, records: payload.r ?? {},
|
||||
txid: tx_hash, height, updatedTxid: null,
|
||||
// the commit this registration revealed, when it had one
|
||||
...(won.commit ? { commit: { height: won.commit.height, txid: won.commit.txid } } : {}),
|
||||
// The mint output's scriptPubKey.address is the initial owner —
|
||||
// needed for the signed-records-manifest write path (POST
|
||||
// /api/records/<name>) so the gateway can reject a signed
|
||||
|
|
@ -918,6 +920,7 @@ export function buildIndexFromSnapshot({ snapshot, enforceTld = false, tldMap =
|
|||
names.set(key, {
|
||||
name: key, category: mint.tokenData.category, records: payload.r ?? {},
|
||||
txid: tx_hash, height, updatedTxid: null,
|
||||
...(won.commit ? { commit: { height: won.commit.height, txid: won.commit.txid } } : {}),
|
||||
owner: ownerFromVout(mint),
|
||||
});
|
||||
// registered straight into the registry covenant?
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue