DVM v9 intrinsics: signature verification, Pedersen commitments, asset balance, point addition (fixes #83) - #84
Closed
liqdmetal wants to merge 4 commits into
Closed
Conversation
…mit, asset_balance, ec_add) Five new DVM-BASIC intrinsics (gated semver >= 9.0.0, so existing contracts are unaffected — the func_table Range mechanism): - verify_sig(pubkey_hex, message, sig_hex) -> Uint64: Ed25519 in-VM signature verification. Enables anonymous contract authorization (caller proves key ownership in encrypted SCDATA at ringsize >= 4, no SIGNER()/ringsize-2) — the K0 Fix C path. Gas 250k. Stdlib, no new deps. - hash_to_point(input) -> String: HashToPoint(HashtoNumber(input)), 33-byte compressed G1 hex, deterministic across nodes. Pinned to the protocol generator derivation (algebra_pedersen.go). Gas 30k. - pedersen_commit(value, blind_hex) -> String + verify_commit(value, blind_hex, commit_hex) -> Uint64: v*G + r*H with NUMS H (hash-to-point of PROTOCOL_CONSTANT+H), 32-byte blind (256-bit hiding). Commit on-chain, reveal off-chain, SC verifies — no oracle trust. Gas 45k. - asset_balance(asset_hex) -> Uint64: reads the SC's OWN stored balance for any asset (incl. DERO) via BalanceLoader(scid, asset). Closes the gap where derovalue()/assetvalue() only report the current tx's incoming value. Gas 2k. - ec_add(p1_hex, p2_hex) -> String: homomorphic accumulation of compressed G1 points — ec_add(c1,c2) == pedersen_commit(v1+v2, b1+b2), enabling commitment-state updates (e.g. AMM reserves) without revealing deltas. Gas 15k. Tests (dvm/verify_sig_test.go): 6 test functions covering valid/tampered/ malformed inputs, determinism, version gate, the homomorphic property, and BalanceLoader wiring. Consensus note: new intrinsics change VM state output -> hard fork (DVM version bump). The version gate means old contracts keep running unchanged. This is the foundation for K0 Fix C and confidential settlement. Also carries the build-manifest fix (go.mod/go.sum): the current tree does not build from a fresh clone.
Author
Related PRs in this K0/DVM seriesThese are part of one coordinated package, deliberately split into independent, reviewable threads:
Ordering logic:
|
This was referenced Aug 22, 2026
liqdmetal
force-pushed
the
feature/dvm-v9-intrinsics
branch
from
August 23, 2026 03:26
61e3da3 to
95f4e30
Compare
liqdmetal
added a commit
to liqdmetal/derohe-improvements-by-liqdmetal
that referenced
this pull request
Aug 23, 2026
…T auto-detect) The consensus half of closing the ringsize-2 loophole for SC calls: a ringsize-2 SC_TX exposes the signer by design (the ring IS sender+receiver, parity selects the sender). This makes ringsize 2 structurally impossible for contracts that don't need it. Design (k0-fix-design.md Fix B2): - SC_META_DATA gains a NoSigner bit (high bit of the Type byte). The 33-byte wire format is UNCHANGED, so existing metadata stays valid and existing contracts default to uses_signer=true (preserving behavior). - At install (transaction_execute.go), the parsed contract AST is scanned for SIGNER() calls (dvm.ContractUsesSigner). No SIGNER() -> NoSigner bit set. Contract authors need no changes. - Consensus enforcement (transaction_verify.go): SC_TX + ringsize 2 + NoSigner-marked contract -> rejected with a clear error. Contracts that genuinely call SIGNER() keep ringsize 2 (owner-gated entrypoints) until the verify_sig migration (K0 Fix C, DVM v9 PR DEROFDN#84) removes that need. Tests (dvm/k0_b2_test.go): - TestContractUsesSigner: detects SIGNER(), no false positive - TestSCMetaNoSignerBit: bit set/clear, 33-byte round-trip, private+NoSigner coexistence Composes with the K0 package: Fix A (DEROFDN#80) warns, Fix B1 (DEROFDN#82) bans ringsize-2 NORMAL/BURN, Fix B2 bans ringsize-2 SC_TX for NoSigner contracts, Fix C (DEROFDN#84 verify_sig) removes the last legitimate ringsize-2 need. Also carries the build-manifest fix (go.mod/go.sum).
Regenerates vendor/ via go mod tidy + go mod vendor (adds missing vendor/modules.txt) so the tree builds from a fresh clone without -mod=mod. Deps unchanged.
community-dev base calls l.Operation.KickReader(), which no published chzyer/readline implements (v1.5.1) -> wallet-cli fails to compile from a fresh clone. Replace with the same UI shim used on the main fork branch: _ = l.Operation (read-unblock helper, wallet-only, not consensus).
This was referenced Aug 23, 2026
Same chain-split class fix as the I3 PR: caller-supplied compressed points are decoded via strictDecodeG1 (x < p validation) so off-curve x>=p encodings are rejected identically across implementations (Go + the clean-room Rust port). verify_commit returns 0, ec_add panics (recovered -> deterministic failure). Canonical points unaffected; full dvm suite green.
This was referenced Aug 24, 2026
Author
|
Superseded by PR #128 — the consolidated intrinsics package. Same code, one reviewable PR with no vendor noise. |
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.
Summary
Hard-fork proposal: five new DVM-BASIC intrinsics gated to a new DVM version (semver >= 9.0.0), turning DERO smart contracts from "oracle-dependent commitments + ringsize-2-only authorization" into "self-contained confidential settlement." Existing contracts are unaffected — the func_table
Rangemechanism hides new functions from old DVM versions.verify_sig(pubkey_hex, message, sig_hex) -> Uint64— Ed25519 in-VM signature verificationhash_to_point(input) -> String— deterministic hash-to-curve (protocol generator derivation)pedersen_commit(value, blind_hex) -> String+verify_commit(value, blind_hex, commit_hex) -> Uint64— Pedersen commitments with 256-bit-hiding blindasset_balance(asset_hex) -> Uint64— read the SC's own stored balance for any assetec_add(p1_hex, p2_hex) -> String— homomorphic point additionWhy these, in order
1.
verify_sig— the missing half of the K0 fixThe only in-VM authorization primitive today is
SIGNER(), which requires ringsize 2 (blockchain/transaction_execute.go) and therefore exposes the sender on-chain. A contract storing a public key and checkingverify_sig(pubkey, msg, sig)in SCDATA lets callers prove key ownership anonymously at ringsize >= 4. This is the "Fix C" path for owner-gated contracts (TransferOwnership, UpdateCode, escrow redemption) — authorization without the anonymity cost.2.
hash_to_point+pedersen_commit/verify_commit— oracle-free commitmentsThe DVM currently expects Pedersen commitments to arrive as external oracles (
dvm_functions.go— the comment documents this as "needs more investigation"). These make commitments a first-class primitive: commit on-chain, reveal off-chain, SC verifies — no trust in the caller's commitment. Generator derivation matches the protocol (HashToPoint(HashtoNumber(PROTOCOL_CONSTANT+"H"))), so commitments are compatible with the existing proof system's generators.3.
asset_balance— the stablecoin/settlement primitiveVerified gap:
derovalue()/assetvalue()only report the value arriving in the current tx (dvm.State.Assets). The chain persists SC asset balances (LoadSCAssetValue/StoreSCValue), but no intrinsic can read them — a contract can't know its own DERO or asset holding before deciding to pay out. This blocks asset-denominated settlement (stablecoins, tokens) entirely.4.
ec_add— homomorphic accumulationPedersen commitments are homomorphic, but DVM-BASIC has no point arithmetic — you can't add two compressed points.
ec_addenables updating a stored commitment by a delta without revealing it (the exact pattern for confidential AMM reserves, batched settlement):ec_add(c1, c2) == pedersen_commit(v1+v2, b1+b2), verified by test.Gas & versioning
verify_sighash_to_pointpedersen_commit/verify_commitasset_balanceec_addAll gated
semver >= 9.0.0— a new DVM version; existing contracts see no change.Tests (dvm/verify_sig_test.go, 6 functions + Fix C wallet half)
verify_sig: valid→1, tampered sig/message→0, wrong key→0, malformed→0 (no panic), version gatehash_to_point: determinism, distinct-input separation, valid 33-byte pointpedersen_commit/verify_commit: determinism, correct reveal→1, wrong value/blind/commit→0, hiding, bindingasset_balance: reads SC's own balance via BalanceLoader(scid, asset), version gateec_add: homomorphic property (ec_add(c1,c2)==c3), commutativity, valid point, version gatewalletapi/sc_auth.go(SCAuthKey helper) +dvm/fixc_auth_test.go— end-to-end owner auth viaverify_sig: attacker key rejected, owner authorized, tampered signature rejectedSecurity notes
verify_sigmust be non-malleable: contract binds the signed message to the call context (domain || txid || args), never signs bare txids. Secret keys never enter the VM.verify_sigdeliberately works on public keys the contract stores — nothing that reveals a caller's key.ec_add,verify_commit) are validated withstrictDecodeG1— x must be < p (canonical field encoding). Go's lenientDecodeCompressedaccepts x ≥ p encodings (computing y from x mod p); a strict decoder (the clean-room Rust port) rejects them. Without the strict check, a contract could feed an encoding one implementation accepts and the other rejects → chain-split class bug. The boundary is pinned in the derohe-rs differential harness (strict_point_decode, 8 vectors). The I3 PR applies the same fix toec_mul.Consensus implications
New intrinsics change VM state output → hard fork (DVM version bump to 9.x). Ship as part of the next scheduled HF; the version gate means old contracts keep running byte-identical.
Not included (deliberately)
verify_proof(ZK verification in-VM) — a much larger undertaking; the native-hook design is documented but deferred.block_hash,verify_merkle,FORloops, cross-contract calls — separate proposals.balance_of(address)(leaks other accounts' encrypted balances), WASM/bytecode VM.Branch:
feature/dvm-v9-intrinsicsin the forkliqdmetal/derohe-improvements-by-liqdmetal. Carries the build fixes: re-vendored modules (vendor/modules.txtpresent) and theKickReaderremoval, so the tree builds from a fresh clone.