Repository navigation
Conversation
…nd a Restored lock's upgrade
…nd expiry sets, and check the key on a restored lock's upgrade
…on and block views
…in the lock coin at a later input
…k outpoint, and skip a transaction that cannot be read
…estored lock, and expire a locked listing whose name expired
…ting purchase on a reorg, and take a reorged sale straight to finalizing or restored
… finalize's bought lock, and say why a dead lock blocks a new one A lock TRANSFER mined but not the owner whose covenant name height is not hsd's info.height belongs to a registration that is gone: the listing is Expired instead of an error on every sync. Before a listing whose name left the lock TRANSFER is aborted, the job looks for a mined purchase (R22). A listing without a lock outpoint is Sold only once hsd shows the coin the purchase spends as a FINALIZE of the name at this listing's lock address, in a block, spending this listing's lock TRANSFER (GET /tx, or the block at info.renewal without the index); find_sale then judges it with that outpoint. A purchase whose spent coin hsd does not show that way leaves the listing as it is. A Sold listing whose purchase is still its sold_txid is not written again, so its updated_at stays and it leaves the after-lock set once the re-check window passes. Lock refuses a name held by a dead Locking row with a message that the earlier lock was never mined and the next sync ends it.
… every mined lead, and reword the dead lock refusal A Locking listing whose lock was finalized elsewhere and bought now reaches Sold: a new guarded write, sell_listing_through_proven_lock, moves only a Locking or ReadyToFinalize listing without a lock outpoint (PROVEN_LOCK_SALE_FROM; SALE_FROM is unchanged) and stores the lock coin finalize_into_lock proved. A sale write that moves no row is logged. With no stored outpoint, every mined purchase found out of our lock address is tried in turn (purchases in the mempool are skipped, an unreadable FINALIZE is skipped) until one is proven to spend this listing's FINALIZE. Lock's refusal for a dead Locking row now says the earlier lock did not go through. Spec section 5 records that, without the transaction index, a dead FINALIZE's sale is not found once the buyer has finalized.
shakedex_restore_lock(name) adopts the name's owner coin as a Restored lock when hsd shows it as a mined FINALIZE of the name at the lock address of the key derived from the seed, at the name's height, with the name not revoked. It refuses an expired or revoked name, a name not at our lock, a name still Locking (pointing at Cancel transfer), a lock coin that is already a TRANSFER, a leftover lock coin from an earlier registration, an owner coin spent in the mempool, a name with an open listing and a lock coin a listing already holds. Every node field it reads fails closed. Recovery-phrase profiles only, with the unlocked signer; the key is derived after the node reads and signs nothing.
`shakedex_import_own_listing_file(text)` turns the active profile's Restored lock into a full Listed listing from its saved listing file. The file is foreign input: it is read by the strict listing-file parser (size limit first) after the recovery-phrase profile and unlocked signer checks, and refused when it charges a market fee (this wallet never writes one), when there is no Restored lock of its name, when its lock outpoint or public key is not the Restored row's, when its payment address is not one of the profile's derived addresses, or when its public key is not the lock key re-derived from the seed for the name (derived after the node read). While hsd's GET /coin still finds the lock coin, every step must verify over it at the value hsd reports, and the coin's outpoint, address and value fail closed; once it is spent only the outpoint and key are checked (plan deviation 6). Nothing is written on a refusal. Spec R32 Enforced/Pinned and SECURITY.md section 5 name the import. Gates: cargo fmt --check, clippy --all-targets -D warnings, cargo test --locked (3168 lib + 3 integration), prettier; each rule mutation-checked.
…x, and restore by name on a fresh profile Adds the live regtest tests for T4: the shakedex CLI buys a Namehold Buy Now and the listing is Sold, follows a reorg of the purchase back to Listed, to SalePending while the purchase is only in the mempool, and to Sold again; a Listed lock is Expired at its name's renewalPeriodEnd (blocks taken back by RewindOnDrop); on a node without --index-tx the listing is Sold through the name's owner and, on a database copy synced after the buyer's FINALIZE, from the block its payment coin was mined in; a fresh profile with a restore's address window restores the lock by name and imports the first device's listing file. Documents them in REGTEST_TESTING.md and adds the expiry test to R22's Pinned line.
… lock outpoint The SalePending and Sold writes no longer fill a missing lock outpoint from the purchase: they move a listing only when the purchase spends its stored outpoint, so only the proven-lock sale gives a listing an outpoint on sale. ReadyToFinalize leaves their source set, since such a listing never carries an outpoint.
Every guarded write that moves a listing's state now takes its source and target states from ListingWrite::transition: the SQL state lists, the refusal of a target the table does not list, and an all-states test that each write moves exactly the pairs pinned as literals. The writes that reopen a listing share one "no other listing of the name is open" condition, and the after-lock job picks the state a SalePending or Sold listing goes back to in one function.
…s its address One rule, ListingLock::holds (covenant type, name hash, lock address, and optionally the name height), now decides that a coin is in or left through a listing's lock: the owner coin before the lock, the lock coin after it, the FINALIZE a proven-lock sale ties to the listing, the purchase's TRANSFER and a restore by name. The owner-coin and lock-coin checks gain the name hash they lacked, so a coin of another name at our lock address (every lock coin of a key shares it) is never adopted as a Restored lock or taken for a mined FINALIZE. Test fixtures now carry the name hash and height items hsd sends.
Every listing write now normalises the txids it stores or compares to lowercase, as hsd writes hashes, so the lock-coin lookup no longer needs lower() in SQL and the jobs, the restore and the listing-file import compare txids exactly instead of mixing case-insensitive comparisons in.
… out of its lock coin A Restored lock without a payment address is now Sold, with the transfer's txid, when the name's owner coin comes from its stored lock outpoint through a mined TRANSFER of the name at the lock address committing to an address not ours, linked input k to output k. The owner's transaction is read with GET /tx or, without the index, from the block at the owner coin's height. A TRANSFER out of our lock coin needs our lock key's signature, so one to an address not ours can only be a price step, which commits to its payment output: the sale was paid to the step's address even though this device does not know it. A transfer to an address of ours (a cancel, T5), one in the mempool, or one from another coin is no verdict.
…the jobs repeated The Owned Names read and the owner-action gate now ask one function whether a listing holds its name (a Locking one only while its lock draft is alive), and the after-lock job reads a listing's stored lock outpoint through one helper. The test-only wrappers for each listing job are gone: tests and the live suite run refresh_listings_with_client, as the sync does. Spec R22 now says that a SalePending listing expires with its name too, pinned by a test, and R19 names the jobs' functions.
…lose the review's minors Importing a listing file onto a Restored lock now refuses one with a non-null feeAddr even beside zero fees, since this wallet writes feeAddr null. The still-locking restore refusal no longer claims the TRANSFER sits at our address. Owned Names returns a failure to read the profile's network instead of hiding the listing states. New tests pin the restore's write-capability gate and Renewals leaving out a name still locking at our address; the live expiry test checks that the Expired row keeps its lock outpoint, and the live reconsiderblock calls and the "through the owner" label are explained. Spec R27 and R32 and REGTEST_TESTING.md follow.
On the regtest node a fresh profile restores a Listed lock by name, so it knows no payment address, and a Namehold buyer buys the first device's file: the restored lock stays Restored while the purchase is in the mempool and is Sold with the purchase's txid once it is mined.
…84 signature A commitment to an address not among the derived ones is no evidence after a restore, whose address window is never extended: another device's cancel to a cancel address past it would have read as a sale. The spending transaction must now show input k's witness as [signature, lock script] with sighash type 0x84 (a price step; a cancel is 0x83) and a payment output at len - 1 - k, the output SINGLEREVERSE commits to; the commitment check stays as an extra one. SpendView carries each input's witness from both of hsd's shapes (REST inputs[].witness, getblock vin[].txinwitness), and a lock input without a readable witness is no verdict. Spec R22, R32 and §5 say so, including the gap when the buyer's FINALIZE is mined before a sync sees the TRANSFER.
…le helper name The sell tests' coin fixture now sends each covenant's full item list as hsd does (four for TRANSFER, seven for FINALIZE), and a live-test comment names the renamed run_listing_step.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #87; merge after it.
Fifth PR of the Shakedex selling stack (spec
docs/specs/2026-10-05-shakedex-name-sales.md, ticket T4): following a listing to Sold (R22), restoring a lock by name and importing its own listing file (R32), and showing locked names in Owned Names. The UI for these comes in T7.What changes
--index-tx: the purchase is read withGET /txor from the block at the height our payment coin, or the name's owner coin, was mined. Payment alone is not Sold; a re-listed lock coin's purchase never sells an old listing.0x84price-step signature into a TRANSFER of the name (a cancel signs0x83).shakedex_restore_lockderives the lock key from the seed and adopts the name as a Restored lock only when hsd shows its owner coin is a mined FINALIZE of this name at that lock address at the name's height; it refuses an expired or revoked name, a name still locking, a lock coin already a TRANSFER, a leftover lock coin, a coin spent in the mempool, and anything already tracked. Recovery-phrase profiles, unlocked signer, a node that can send.shakedex_import_own_listing_fileupgrades a Restored lock from its saved file when the outpoint and public key match the restored lock and the key re-derived from the seed, the payment address is one of the profile's, and (while the lock coin is unspent) every step's signature verifies at the coin's value on hsd; a market fee or fee address is refused.Checks
cargo fmt --check,cargo clippy --locked --all-targets -D warnings,cargo test --locked3188 passed / 0 failed,pnpm test,tsc -b, prettier on tracked files — all pass.live_suite withHNS_IT_SHAKEDEX=1and the second node without--index-tx— 112 passed, 0 failed, 0 skipped (chain 0 → 12648). New: the CLI buys a Namehold listing → Sold, then a reorg of the purchase's block → Listed, the purchase handed back → SalePending, mined → Sold; a listed lock expires with its name (checked against hsd'srenewalPeriodEnd, chain rewound even on panic); Sold detected on the node without the transaction index (the test asserts the node does not find the mined transactions by txid); restore by name on a fresh profile and import of the file; a lock restored by name and bought is Sold.git diffclean): every guard of every state write and the transitions table; the owner and payment paths of the sale (incl. a re-listed coin), the stored-outpoint requirement, the0x84evidence for a restored lock, every reorg way back, expiry, the stable-Sold no-rewrite, every restore refusal and import rule, the Owned Names and Renewals rules; the live ones (sale not recorded, block fallback removed, reorg not reverted).Not checked here / known gaps (in spec §5): a dead FINALIZE's sale on a node without the index after the buyer finalized; a restored lock whose buyer finalized before a sync stays Restored; a cancel mined from another device is handled in T5; three cheap pre-filters in the sale search are redundant with the decisive check, so their mutations survive by design.