Add SLP validity validation - #67
Conversation
Add consensus-grade SLP validity verdicts to the index, which previously transcribed SLP messages without judging them: - Strict spec parser and pure validator (ref/bitcoin/tx/slp) for token types 0x01/0x41/0x81, big.Int sums, validated against the full slp-unit-test-data corpus (vendored in testdata) - Per-tx slp_validity topic: status+reason bytes, absence = pending, verdicts final (DAG-validity); fail-to-pending, never fail-to-wrong - Batch orchestrator (node/act/slp_validate) resolving inputs via slp rows, parent verdicts, geneses, and TxProcessed - Validation wired into the SLP op_return handler on both the mempool and confirmation passes; saver no longer skips addr-independent handlers when no input address parses; SLP prefix match now accepts non-minimal lokad pushes - maint slp-validity-sweep: height-ordered backfill/safety-net with per-block fixpoint, completed-height cursor (explicit --start never moves it), and exhaustive per-shard height-block range pagination (chain.GetHeightBlocksRange) - GraphQL validity field (VALID|INVALID|PENDING) on SlpOutput/SlpBaton - Register missing slp.Send topic; store declared TokenType on slp.Mint/slp.Send Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cascade validation (slp_validate.ValidateTxsCascade): when a tx's verdict lands, spenders of its outputs are looked up and any pending SLP spenders are validated recursively, so a decided ancestor immediately resolves pending descendants of any depth on the live save path. Race-free because spender rows are written by the first saver before validation runs. The slp-validity-sweep is redesigned around the index's own datasets instead of walking blocks by height: the default mode iterates the slp genesis/mint/send topics per shard and validates txs with no verdict (the routine safety net); --audit scans every chain tx output for an SLP lokad script, leniently transcribing and validating anything the live path missed (historical backfill / deep audit). Audit resume uses a per-shard uid cursor, cleared on completion, and ignores wrong-format legacy values (the old sweeper stored an 8-byte height under the same status). Visit order is irrelevant in both modes because the cascade resolves child-before-parent chains, so the height walker and its completed-height cursor machinery are deleted. An end-to-end suite test (test/tasks slp_validity, runs under go test) drives the full flow through real in-process queue shards: token lifecycle, fake send/mint, invalid-parent cascade, out-of-order arrival, 4-generation chains, index-sweep and audit backfills incl. an untranscribed gap tx and the legacy-cursor guard, and NFT1 group/child. Suite lifecycle fixes make it repeatable in one process: synchronous listener bind, store cache cleared on suite end, partial startup unwound, and Server.Stop closes the listener when it beats a backgrounded Serve. Also fixes a nil-Context panic in populate_p2sh's GetBlockTxs call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GetHeightBlocksRange and paginateHeightBlocks were built for the abandoned height-ordered sweeper design; the dataset-driven sweep never touches height blocks. Also drop the never-read Result.Skipped counter and update doc comments that still described the old design. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| } | ||
| copy(s.TokenHash[:], jutil.ByteReverse(data)) | ||
| copy(s.TokenHash[:], jutil.ByteReverse(data[:memo.TxHashLength])) | ||
| if len(data) > memo.TxHashLength { |
There was a problem hiding this comment.
Should this reverse compatibility be added to dbi/item/slp/send.go and other SLP items for the new TokenType? With it, when reading previously saved tokens it will think token type is 0 when really it just was never set. Also, during db item deserialization should we allow use of if len(data) < X when there are no optional fields? Should we be checking for exact match (if len(data) != X)? Otherwise you're reading an item that has extra data, which should we assume we don't know (or is malformed) and abort?
| @@ -0,0 +1,290 @@ | |||
| // Package slp_validate resolves the inputs of SLP transactions against the | |||
There was a problem hiding this comment.
Let's audit this validation logic against the spec one more time.
|
|
||
| // NeedsAddr reports whether the handler requires an input address to be resolved. | ||
| // SLP transcription/validation is address-independent, so SLP txs are indexed | ||
| // even when no input unlock script yields an address (e.g. P2SH inputs). |
There was a problem hiding this comment.
Why would some SLPs not have an address? Is this like if they're sent to non-standard outputs or something? (like not P2SH, P2PKH, etc.)
SLP db items now require exact serialized lengths (TokenType always written), genesis rows store ticker/name/docurl as uint16 length-prefixed bytes (byte-faithful, handles 0x00 and empty fields), and the validator resolves unknown parents via the vout-0 chain output lokad check instead of TxProcessed, so missing slp rows read as pending, never invalid. Backward compatibility is intentionally dropped: deployment wipes the slp topics and repopulates from chain data. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add consensus-grade SLP validity verdicts to the index, which previously transcribed SLP messages without judging them: