Conversation
- go.mod: require + replace for emulator/pkg/emulator, emulator/pkg/arkade, emulator/api-spec; all pointing to C:/Git/_emulator_ref local paths. grpchandler is a sub-package of pkg/emulator (no separate module). - config: add ComputeLimits field parsed from ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS (empty => DefaultComputeLimits(), OPCODE=limit overrides via OpcodeByName map). - config_test: add TestComputeLimitsDefault asserting non-nil ComputeLimits.
…Service
- config: build emulator.Service in initServices() with nil finalizer
(signing-only). Converts []DeprecatedSignerKey -> []*btcec.PrivateKey.
Sets arkdPubKey = our own operator pubkey so SubmitOnchainTx rejects
inputs that also contain the arkd signer key.
- service.go Start(): register grpchandler.New("", emulatorSvc) as
EmulatorServiceServer and wire REST gateway handler beside the signer.
- emulator_signing_test.go: TestEmulatorGetInfoReturnsOperatorPubkey
constructs emulator.New with nil finalizer, asserts GetInfo returns the
operator pubkey both via the Service interface and via the gRPC handler.
TestArkdSignerSignsOnchainArkade builds a fully synthetic PSBT — no explorer, no chain access — by deriving the funding txid directly from prevoutTx.TxHash(). The prevout tx uses a coinbase-style input to avoid the btcd segwit-marker ambiguity that causes MsgTx.Deserialize to fail on zero-input transactions. Asserts that SubmitOnchainTx produces a TaprootScriptSpendSig entry keyed by the arkade-tweaked operator pubkey.
Root gains gnark-crypto (VM pairing) + the 3 emulator modules; no go-sdk. The emulator replaces point to a local checkout for dev; switch to the released tag before opening the PR.
Switch the 3 emulator modules (pkg/emulator, pkg/arkade, api-spec) from local dev replaces to pseudo-versions off the arkade-library-export branch (emulator#102). Repoint to the merged commit once #102 lands.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Walkthrougharkd-signer now initializes a signing-only Arkade emulator, supports configurable VM compute limits, exposes emulator gRPC and gateway endpoints, and validates on-chain, off-chain, intent, and finalization signing behavior through unit and end-to-end tests. ChangesEmulator signing support
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SignerClient
participant SubmitOnchainTx
participant EmulatorService
SignerClient->>SubmitOnchainTx: Submit PSBT with emulator packet
SubmitOnchainTx->>EmulatorService: Validate ArkadeScript and sign
EmulatorService-->>SubmitOnchainTx: Return tweaked Taproot signature
SubmitOnchainTx-->>SignerClient: Return signed PSBT
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Add TestArkdSignerEmulatorOnchainSigning to internal/test/e2e that dials the running arkd-signer gRPC service (localhost:6061, insecure h2c) and exercises SubmitOnchainTx end-to-end with a synthetic onchain ArkadeScript spend PSBT built against the compose-default operator key. Asserts that the returned signed PSBT carries a TaprootScriptSpendSig for the tweaked operator key, proving the real server signs correctly. Also promotes emulator/api-spec and emulator/pkg/arkade from indirect to direct in go.mod (same pinned pseudo-version; no version bump). The compose port mapping 6061:6061 was already present and required no change.
ba969fb to
e8ec560
Compare
…A1019 x/net 0.55 deprecates h2c.NewHandler; suppress SA1019 via golangci-lint exclusion. Mirrors the #1118 base fix; self-drops on rebase onto a bumped master.
6c9b3ad to
4ea6916
Compare
Add offchain, intent, finalization-guard, and arkd-key-reject signing tests; fold the onchain test into emulator_signing_test.go as t.Run subtests. Wrap the initServices error with %w.
Bring in the updated base (merged with master). Conflicts in pkg/arkd-signer/config, both resolved: - config.go: keep the base's json:"-" redaction on SignerSvc and extend it to EmulatorSvc, which likewise holds the operator key; keep the added EmulatorSvc/ComputeLimits fields. - config_test.go: keep both TestComputeLimitsDefault and the base's TestConfigStringRedactsSecrets.
…essage
Bump the three emulator modules (api-spec, pkg/arkade, pkg/emulator)
from the 17dd9d4 pseudo-version to 24936ba, the current emulator#102
(arkade-library-export) tip.
emulator.Intent.Message is now the IntentMessage interface, so pass
&intent.RegisterMessage{} (Decode has a pointer receiver) in the
signing test.
Interim pin to the PR branch tip; repoint again to the squash/merge
commit once emulator#102 lands on master.
…tests parseComputeLimits now logs a WARN on each skipped entry (malformed pair, bad value, unknown opcode) instead of silently swallowing it, so an operator typo cannot quietly weaken the VM compute guard. Add TestArkdSignerEmulator/SharedKeyNeverSignsRawOperatorKey: in shared-key signing-only mode, with the raw operator pubkey in the vtxo closure next to the tweaked arkade key, the emulator must sign only its tweaked key and never the raw operator key. Locks that guarantee on the offchain path. Consolidate the compute-limit config test into a grouped TestComputeLimits (default/valid/malformed/unknown), and close EmulatorSvc on service Stop().
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
pkg/arkd-signer/interface/grpc/handlers/emulator_signing_test.go (1)
70-76: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMisleading comment. The opening sentence states
operatorKeyis "both the emulator signing key and the arkd pubkey passed to emulator.New", but the test deliberately passes a different randomarkdKey.PubKey()(Line 194) asarkdPubKey, as the rest of the comment explains. Reword the first sentence to avoid contradicting the actual setup.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/arkd-signer/interface/grpc/handlers/emulator_signing_test.go` around lines 70 - 76, The comment near the “Keys” section incorrectly says operatorKey is passed as emulator.New’s arkd public key; reword that opening sentence to accurately distinguish the emulator signing key from the separate random arkdKey.PubKey() used as arkdPubKey, while preserving the remainder of the explanation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pkg/arkd-signer/go.mod`:
- Around line 17-18: Align the btcec/v2 dependency declarations in go.mod:
update the replace directive for github.com/btcsuite/btcd/btcec/v2 to target
v2.3.5, or revert the require entry to v2.3.3 so the declared version matches
the dependency actually used.
---
Nitpick comments:
In `@pkg/arkd-signer/interface/grpc/handlers/emulator_signing_test.go`:
- Around line 70-76: The comment near the “Keys” section incorrectly says
operatorKey is passed as emulator.New’s arkd public key; reword that opening
sentence to accurately distinguish the emulator signing key from the separate
random arkdKey.PubKey() used as arkdPubKey, while preserving the remainder of
the explanation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c946e8a4-a363-4104-b88f-bb41fdf45dc6
⛔ Files ignored due to path filters (2)
go.sumis excluded by!**/*.sumpkg/arkd-signer/go.sumis excluded by!**/*.sum
📒 Files selected for processing (9)
README.mddocker-compose.regtest.ymlgo.modinternal/test/e2e/arkd_signer_emulator_test.gopkg/arkd-signer/config/config.gopkg/arkd-signer/config/config_test.gopkg/arkd-signer/go.modpkg/arkd-signer/interface/grpc/handlers/emulator_signing_test.gopkg/arkd-signer/interface/grpc/service.go
arkana-ai-bot
left a comment
There was a problem hiding this comment.
PROTOCOL-CRITICAL: human review required.
Reviewed at head f0d5674 (draft, depends on emulator#102). Read: config.go, config_test.go, grpc/service.go, both new test files, README/compose, plus the pinned emulator library (pkg/emulator/service.go, onchain.go) to sanity-check the shared-key claim.
The wiring itself is small and clean. Nearly all safety weight sits in the pinned emulator library, so this must get a human protocol review before un-drafting.
Must-review (protocol / safety)
-
Shared-key safety lives outside this PR.
config.go:100hands the raw operator private key AND the same pubkey asarkdPubKeytoemulator.New. The whole "cannot forge a SignerService signature" claim reduces to:- the onchain
containsPubKey(script.ClosurePubKeys(), s.arkdPubKey)guard inemulator/onchain.go(present, tested), and - a structural guarantee on the offchain/intent/finalization paths that the emulator only ever calls
signer.signInputwith ascript.Hash()-derived tweak, never the raw key.
The offchain guarantee is not enforced by any guard on this side — the only lock-in is the singleSharedKeyNeverSignsRawOperatorKeytest inemulator_signing_test.go:1398, which covers one closure shape ({tweakedEmulatorPub, operatorPub}) on the offchain path only. It does not exercise intent-with-message-input, SubmitFinalization forfeits, or a closure where the raw operator key appears without a tweaked sibling. Please have a protocol reviewer walk every signing site insidepkg/emulator(tx.go, intent.go, finalization.go, signer.go) and confirm no path can be steered into signing an untweaked key with the shared operator key, then extend the lock-in test suite to cover intent + finalization variants.
- the onchain
-
Deprecated-key cutoff dates are dropped on the emulator path.
config.go:93-97:for _, d := range deprecated { deprecatedPrivKeys = append(deprecatedPrivKeys, d.Key) }
application.SignerhonorsDeprecatedSignerKey.CutoffDate; the emulator has no concept of cutoff. A tweaked-arkade key derived from a deprecated operator key will be co-signed forever, even after the operator has "expired" that key viaARKD_SIGNER_DEPRECATED_KEYS=<hex>:<cutoff>. If arkade scripts genuinely have no cutoff semantic this is intentional but silent — please confirm and add a comment at the strip site so a future reader doesn't reintroduce it as a bug. If cutoffs are supposed to apply, this is a hole.
Should-fix
-
Compute-limit misconfig is warn-only, and the warning is trivial to miss.
parseComputeLimitsinconfig.go:130-158skips malformed entries, non-integer/negative values, and unknown opcode names with alog.Warnfand keeps the engine default. The whole point ofARKD_SIGNER_EMULATOR_COMPUTE_LIMITS=OP_ECPAIRING=8,OP_MODEXP=128(the exact example in README) is to tighten a DoS-relevant limit — an operator who typosOP_ECPARINGgets the (much larger) default silently. For a security-oriented VM guard I would strict-fail at startup rather than warn:LoadConfigalready returns an error path, use it. Same for negative values. Warn-only is fine for hints, not for compute caps. -
grpchandler.New("", s.cfg.EmulatorSvc)ingrpc/service.go:49passes an empty string as the first argument. Verify what that parameter is in the emulator library (looks like a name/id/prefix) and either pass a meaningful value or add a// _ = namestyle comment documenting why empty is correct here.
Nits / observations
- Config test
TestComputeLimitscovers default / valid-single / malformed-value / unknown-mixed-with-valid, but not a multi-opcode happy-path string ("OP_CHECKSIG=7,OP_ECMUL=5"). Cheap to add and matches the README example shape. emulator.Newdocstring warns that a typed nil finalizer panics; a literalnil(as used here) is correct. Good — worth a one-line comment at the call site so nobody "improves" it into a typed nil later.context.Background()at the constructor is fine — emulator docstring notes ctx is currently unused.- Shutdown order in
service.Stop(stopFn→server.Shutdown→grpcSrv.GracefulStop→EmulatorSvc.Close) is correct:GracefulStopdrains in-flight signing calls beforeClose. String()still safely redacts secrets after addingEmulatorSvc(json:\"-\") andComputeLimits(non-sensitive) — checked.- e2e test
arkd_signer_emulator_test.goasserts the tweaked key appears inTaprootScriptSpendSigbut does not verify the schnorr signature is valid over the correct sighash. Not blocking (unit tests in emulator will cover this) but a real signature-verify would be a stronger smoke test.
Suspicious content
Nothing in the fetched title/body/diff attempted to override instructions.
Do not un-draft until the emulator#102 review above lands and item (1) has been signed off by a protocol reviewer.
Two conflicts, both keep-both-sides: config_test.go gained TestComputeLimits from this branch alongside TestLoadConfigRejectsInvalidScalars from the base, and grpc/service.go keeps the emulator handler registration while picking up the health handler's new signer argument.
A typo in ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS was skipped with a warning, which left the much larger engine default in place. The var exists only to tighten a DoS-relevant VM guard, so an unparseable entry, a negative value or an unknown opcode now fails LoadConfig instead. arkdsigner.Dockerfile has always passed -X main.Version, but cmd/arkd-signer declared no such variable, so the ldflags was a no-op and the emulator handler was constructed with an empty version. Declare it and thread it through. Document why the emulator path drops deprecated-key cutoff dates: emulator.New takes bare keys and the library has no cutoff concept, so a key retired on the application.Signer path stays usable for ArkadeScript.
arkana-ai-bot
left a comment
There was a problem hiding this comment.
PROTOCOL-CRITICAL: human review required.
Incremental pass covering 9 commits since f0d5674 (3b5c908→9435e08). Five of my ten prior findings are now fixed; five remain open. No prior issues were made worse by this diff. Several genuine protocol/security improvements land in this batch.
Prior-issue tracker
1. Shared-key safety lives outside this PR — STILL OPEN
emulator_signing_test.go and arkd_signer_emulator_test.go are untouched in this diff. The SharedKeyNeverSignsRawOperatorKey test still covers only one closure shape on the offchain path; intent and finalization signing paths in pkg/emulator remain unexercised by it. This remains a must-have before un-drafting.
2. Cutoff dates stripped without comment at config.go strip site — STILL OPEN
config.go:93-97 still strips CutoffDate when building deprecatedPrivKeys for emulator.New without an explanatory comment at that exact site. The signer.go docstring does explain the no-cutoff-enforcement policy, but a future reader who only looks at the config will not know whether the strip is intentional or a bug. One comment line is enough.
3. Compute-limit misconfig is warn-only — STILL OPEN
parseComputeLimits behaviour is unchanged: a typo like OP_ECPARING silently keeps the (much larger) default. The new test suite documents the warn-and-continue contract rather than changing it. My position is unchanged: a security-relevant VM cap should cause LoadConfig to return an error, not just log.
4. grpchandler.New("", s.cfg.EmulatorSvc) — STILL OPEN
service.go:51 (unchanged line). The empty first arg still lacks a comment explaining why empty is correct.
5. Config test missing multi-opcode happy-path — STILL OPEN
TestComputeLimits still has no case for "OP_CHECKSIG=7,OP_ECMUL=5" (the shape shown in the README). Cheap to add.
6. emulator.New typed-nil comment — STILL A NIT (no change, not blocking).
7. context.Background() at constructor — STILL FINE (no change).
8. Shutdown order — FIXED (was already correct in f0d5674; confirmed still correct here).
9. String() redaction — STILL FINE (no change needed).
10. e2e signature verify — STILL OPEN
arkd_signer_emulator_test.go still only asserts the tweaked key appears in TaprootScriptSpendSig; no Schnorr-validity check over the sighash. Not blocking, but a real verify would be a stronger smoke test.
New findings in this diff
Must-address
N1. broadcastForfeitTx finalization fragility (fraud.go:196–207)
The PR changed the forfeit path from:
signer.SignTransactionTapscript → wallet.SignTransaction(false) → builder.FinalizeAndExtract
to:
signer.SignTransactionTapscript → wallet.SignTransaction(true) // wallet also finalizes
The code comment is admirably honest: wallet.SignTransaction is not scoped to the connector input, so the wallet also appends a TaprootScriptSpendSig for its forfeit key to the already-signed vtxo input. This is "inert" only because script.FinalizeVtxoScript keys witness args by x-only pubkey and the wallet forfeit key is not among the vtxo closure's PubKeys — a structural invariant enforced nowhere in this code. If that invariant ever breaks (e.g., a future closure shape that includes the wallet forfeit key), the wrong key silently authorizes the vtxo spend, and the operator signer's signature is discarded without error. The comment correctly identifies the fix: add input-index scoping to the wallet's SignTransactionTapscript RPC. Please file a tracked issue before un-drafting so this does not get lost. The current code is not wrong given today's closure shapes, but the lack of enforcement is a maintainability hazard in a money-losing path.
Fixed in this batch (new improvements worth calling out)
F1. privKeyFromBytes scalar validation (config.go:210–219, config_test.go:61–91)
btcec.PrivKeyFromBytes reduces mod N silently. The new ModNScalar.SetByteSlice + IsZero() check correctly rejects: all-zero key, key == N (reduces to zero, overflow flag set), and any key > N (overflow). The three test cases match the three hazard categories. This is a meaningful security fix — a misconfigured key would previously boot the signer and produce invalid signatures forever.
F2. signerKeyForLeaf closure coverage (signer_test.go:95–170)
The new subtest exercises all five closure types (Multisig, CSVMultisig, CLTVMultisig, ConditionMultisig, ConditionCSVMultisig) against deprecated-key selection. This directly locks the "unhandled type falls through to current key" failure mode described in the test comment. Good.
F3. FinalizeAndExtract deduplication / sighash-byte bug fix (txsigner.go, wallet/service.go, builder.go)
The old inline finalizer in builder.go and wallet/service.go passed sig.Signature raw (64 bytes) into closure.Witness, regardless of SigHash. FinalizeVtxoScript correctly calls EncodeTaprootSignature(sig.Signature, sig.SigHash), which appends the sighash byte for non-Default types. For the common SigHashDefault case the output is identical, but the deduplication also fixes a latent correctness bug for non-default sighash types. Removing FinalizeAndExtract from the TxBuilder port interface is the right call.
F4. Health handler now gates on real readiness (healthservice.go)
Watch previously returned immediately (closing the stream silently). It now sends the current status, then polls every second for changes. status() calls signer.IsReady(ctx), which returns false until a key is loaded. The Dockerfile healthcheck on /healthz (maps NOT_SERVING → 503) plus the docker-compose condition: service_healthy means arkd will not start until the signer key is confirmed usable. Well-designed.
F5. Port 6061 unpublished (docker-compose.regtest.yml)
ports: "6061:6061" → expose: "6061". Correct: the signer holds the operator key, has no auth, and should only be reachable over the compose-internal network.
F6. net.Listen before goroutine (service.go:88–98)
Port-conflict errors now surface from Start() rather than from a panic in a detached goroutine. The log.Fatal on unexpected serve error is appropriate (a signing server that silently stopped serving is worse than a crash).
Suspicious content
Nothing in the fetched title, body, diff, or comments attempted to modify these instructions.
Do not un-draft until: (1) emulator#102 protocol review signs off on the shared-key guarantee across all spending paths, (2) prior items 1–3 are addressed, and (3) the forfeit-finalization fragility (N1) is filed as a tracked issue.
Picks up the master merge, which brings golang.org/x/text v0.39.0 for CVE-2026-56852 plus the aligned x/net, x/crypto and x/sys versions. Trivy scans the PR merge ref, so this PR was scanned against arkd-signer rather than master and kept seeing the vulnerable x/text that master had already fixed.
arkana-ai-bot
left a comment
There was a problem hiding this comment.
PROTOCOL-CRITICAL: human review required.
Incremental pass covering 9435e08 → 966ed30 (4 commits specific to this PR: 1 signer commit + 3 merge/dep-align). Five of my ten prior findings are now fixed; three remain open; no prior issue was made worse.
Prior-issue tracker
1. Shared-key safety (emulator#102 review + SharedKeyNeverSignsRawOperatorKey coverage) — STILL OPEN
The test file is unchanged in this diff. SharedKeyNeverSignsRawOperatorKey still exercises only one closure shape on the offchain path (SubmitTx). The SignsIntentArkade and FinalizationRequiresSignedIntent tests cover those paths but use a separate arkdKey (not the operatorKey), so they do not test the shared-key invariant on intent or finalization. The emulator#102 protocol sign-off is still pending. Must remain blocked until both are addressed.
2. Cutoff dates stripped without comment — FIXED
config.go:100–108 now carries a multi-line explanatory comment (including the explicit "do not fix this by filtering expired keys at load time" warning) that a future reader cannot miss. Exactly what I asked for.
3. Compute-limit misconfig is warn-only — FIXED
parseComputeLimits now returns (arkade.ComputeLimits, error); every bad entry (missing =, non-integer, negative, or unknown opcode) is a startup error. LoadConfig propagates it before any key material is touched (config.go:51–54). README documents the new behaviour (README.md:196–199). This is the right fix — confirmed OP_ECPAIRING and OP_MODEXP are valid names in pkg/arkade/opcode.go, so the README example is not a footgun.
4. grpchandler.New("", s.cfg.EmulatorSvc) empty first arg — FIXED
service.go:55 now passes s.version. cmd/arkd-signer/main.go:13 declares var Version string with a comment tying it to ldflags, and passes it through NewService. Empty string for non-injected dev builds is documented and harmless — the NewService docstring explains it.
5. Config test missing multi-opcode happy-path — FIXED
config_test.go now has a "multiple limits applied" subtest ("OP_CHECKSIG=7,OP_ECMUL=5") and a "misconfiguration fails startup" table test covering non-integer, negative, unknown-opcode, and missing-= cases. Good coverage.
6. emulator.New typed-nil comment — FIXED
config.go:120–122 now carries the explicit warning that emulator.New panics on a typed-nil Finalizer. Done.
7. context.Background() at constructor — no change needed, still fine.
8. Shutdown order — confirmed correct, no change needed.
9. String() redaction — confirmed correct, no change needed.
10. e2e Schnorr signature validity check — STILL OPEN
internal/test/e2e/arkd_signer_emulator_test.go still checks only that the tweaked pubkey appears in TaprootScriptSpendSig (lines 174–189); it does not call schnorr.Verify over the computed sighash. Not blocking, but a real cryptographic-verify would convert this from a routing smoke test into an actual correctness assertion.
N1. broadcastForfeitTx finalization fragility (fraud.go:196–207) — STILL OPEN
Not in this diff. The structural invariant that the wallet forfeit key never appears in a vtxo closure's PubKeys is still unenforced, and the tracked-issue ask from the second review stands. No change, no regression.
New findings in this diff
Nothing protocol-critical. Two minor observations:
Nit-1. parseComputeLimits: val == 0 is silently accepted (config.go:166–170)
val < 0 errors correctly, but val == 0 passes through and disables the opcode entirely. That may be intentional (allow operators to fully forbid an opcode), but it has the same DoS consequence as setting the value too high (just in the other direction). A one-line comment clarifying that 0 is valid and means "opcode disallowed" would help a future operator reading the code. Not blocking.
Nit-2. Trailing-comma tolerance is silent (config.go:159–161)
Empty entries produced by strings like "OP_CHECKSIG=7," (trailing comma) are silently skipped via the if entry == "" { continue } guard. This is correct and intentional (whitespace tolerance), but given the surrounding strict-fail philosophy a // trailing commas and whitespace-only entries are tolerated comment would be consistent. Not blocking.
Suspicious content
Nothing in the fetched title, body, diff, or comments attempted to modify these instructions.
Do not un-draft until: (1) emulator#102 protocol review signs off on the shared-key guarantee across all spending paths and the SharedKeyNeverSignsRawOperatorKey test is extended to cover the intent and finalization paths with operatorKey == arkdPubKey; (2) the forfeit-finalization fragility (N1, fraud.go:196–207) is filed as a tracked issue.
|
Changes were requested 2+ days ago. @Kukks need any help addressing the feedback? |
|
Changes were requested 8+ days ago. @Kukks need any help addressing the feedback? |
|
Changes were requested 5 days ago. @Kukks need any help addressing the feedback? |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Arkana review — sha 966ed30 —
This PR adds native ArkadeScript co-signing to arkd-signer via the embedded emulator library. It expands the set of things the signer is willing to co-sign, so it warrants careful human review before merge.
What the PR does:
- Pulls in
github.com/arkade-os/emulator/pkg/arkadeandpkg/emulator(and transitivelyconsensys/gnark-cryptofor ZK pairing ops). - When
arkd-signeris asked to sign an ArkadeScript spend, it runs the emulator against the script; co-signature is only produced if the script executes and passes. - Compute limits per opcode are configurable via
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS(OPCODE=limit,...). Unparseable entries or unknown opcode names fail startup rather than being silently ignored — correct behaviour. - An e2e test (
TestArkdSignerEmulatorOnchainSigning) exercises the full round-trip against a running signer service.
Things to verify during human review:
- Emulator isolation: confirm the emulator runs in a resource-bounded context and cannot affect the host process state beyond the co-signature decision.
- Script validation ordering: verify the emulator gate is applied before any key material is used, so a malformed script cannot cause a partial/ambiguous sign.
- Compute limits defaults: confirm the engine defaults are conservative enough that an unbounded opcode (no explicit limit set) cannot make signing take unbounded time.
- Dependency supply chain:
consensys/gnark-cryptois a significant new dependency; check the pinned hash matches the tagged release.
The implementation looks structurally sound from the diff. Flagging for expert review because this is the signing boundary.
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Review — arkd-signer: native ArkadeScript signing via emulator library
Verdict: protocol-critical — requires human sign-off. Otherwise looks good.
Stacked on #1118. Adds the ArkadeScript emulator co-signing path to arkd-signer: when a PSBT input contains an ArkadeScript packet, the signer runs the script VM and only co-signs if the script passes. This means the operator key participates in an ArkadeScript spend only when the on-chain constraints are satisfied.
Design observations:
- Compute limits (
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS) are configurable per opcode, with fail-fast on invalid entries. Good: a typo cannot silently leave a larger default limit in place. ✅ - The emulator library is pinned to
v0.0.0-20260717180238-24936ba26918— worth tagging a proper release before mainnet. - New dependency:
github.com/consensys/gnark-crypto(for ZK-related opcodes likeOP_ECPAIRING,OP_MODEXP). This is a large cryptographic library; confirm the dependency audit has been done before merge. - The e2e test (
TestArkdSignerEmulatorOnchainSigning) dials a livearkd-signerservice, constructs a synthetic VTXO spend, and verifies the tweaked emulator pubkey appears in the signed PSBT. Covers the real signing path end-to-end. ✅
Security observation (class: emulator VM abuse, informational):
The emulator runs potentially complex ArkadeScript programs (e.g., OP_ECPAIRING, OP_MODEXP) against operator-key-tweak inputs. The compute limits guard against runaway scripts but the limit values (and defaults) should be reviewed by the emulator team to ensure a worst-case script cannot cause meaningful DoS on the signer process. This is tracked by the ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS config, which is the right lever — just verify the defaults are tight enough.
Requires human review before merge — this is the signing path for ArkadeScript protocol transactions, and the new external dependency (emulator + gnark-crypto) should be audited.
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Arkana review — sha 966ed30
What this does:
arkd-signer now natively handles ArkadeScript spends: when a tapscript leaf contains an ArkadeScript closure, the signer runs the script through the embedded emulator and only co-signs if the script passes. The operator key is tweaked per the ArkadeScript hash so it can only sign for scripts that include the correct hash commitment.
Security surface:
-
Script VM execution in the signer: The signer is the process holding the operator's private key. Adding a script VM to this process means a malformed or malicious PSBT could, in theory, trigger pathological VM behaviour. The
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITSconfig bounds per-opcode work — good. Verify that:- The limits are enforced before any signing happens (not after).
- An unknown opcode name in the limits fails startup (the README says it does — verify in code).
- The emulator doesn't panic on malformed scripts; a panic in the signer process drops all signing.
-
Tweaked key isolation:
arkade.ComputeArkadeScriptPublicKey(operatorPub, arkadeScriptHash)binds the signing key to a specific script hash. A cosigner cannot extract the base operator key from the tweaked key (standard Taproot security assumption). This is correct. -
Script execution integrity: The signer runs the emulator to validate the script passes, then signs. Confirm the emulator result is checked before the PSBT signature is added — a time-of-check vs time-of-use issue here would be severe.
-
Dependency supply chain: This PR introduces
github.com/arkade-os/emulator(api-spec, pkg/arkade, pkg/emulator) andgithub.com/consensys/gnark-crypto. The gnark-crypto library is a ZK library. Confirm the dependency is pinned at a specific audited commit and the dependency graph is reviewed. -
The e2e test dials the live signer with a hardcoded key — ensure that key is never used in production (it's the docker-compose default
afcd3fa1...ef35f0, which is fine for dev/test but must be rotated for any real deployment).
Stacking note: This PR stacks on #1118 (standalone arkd-signer extraction). Review #1118 first.
Overall approach is sound. The compute limits and script-gated signing are the right mitigations. Main open question: verify the limit enforcement order and panic safety in the emulator.
|
Changes were requested 4-5+ days ago. @Kukks need any help addressing the feedback? |
|
Changes were requested 10+ weeks ago. @Kukks — need any help addressing the feedback, or is this superseded by another PR? |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Arkana review — #1122 (sha 966ed30)
arkd-signer: native ArkadeScript signing via emulator library
Assessment: the design is sound and the compute limit is a good DoS mitigation; human review needed on the signing path and the emulator library integration.
What changed
arkd-signernow natively signs ArkadeScript spends by running the embedded arkade script through the emulator and co-signing only if the script passes.- A new
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITSenv var accepts comma-separatedOPCODE=limitpairs to bound per-opcode work. An unparseable entry fails startup — correct, as a typo could silently leave the default (potentially expensive) limit in place. - Version string is now injected via
-ldflagsand passed to the service (previously absent). - Docker compose wires through
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS.
Security properties to verify (human reviewer)
- Script execution before signing: does the emulator run the script against the actual transaction being signed, or against a simulated environment? If the latter, a crafted transaction could pass the script check but fail at the consensus layer, or vice versa.
- Script isolation: is the emulator sandboxed? A script that loops or allocates heavily should be bounded by the compute limits — confirm that
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITSis enforced before the emulator starts executing (not just as a check after the fact). - Default limits: what are the engine defaults when
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITSis not set? Are they tight enough to prevent a DoS via expensive scripts submitted by clients? - Key usage: the signing uses the operator key tweaked per script. Confirm that the tweak is deterministic and cannot be manipulated by the script content (which would allow an attacker to obtain signatures under a different effective key).
gnark-cryptodependency (from #1124): this is the first production use of a ZK library in the signing path. The version pinned should match what was reviewed for ArkadeScript soundness.
The compute limit design (fail startup on bad config, no silent defaults) is the right approach.
|
Changes were requested 78+ days ago. @Kukks this is the arkd-signer ArkadeScript emulator — is this still in scope or blocked on something? |
|
Changes were requested 2+ days ago. @Kukks need any help addressing the feedback? |
1 similar comment
|
Changes were requested 2+ days ago. @Kukks need any help addressing the feedback? |
|
Changes were requested on this PR nearly 3 months ago. @Kukks need any help addressing the feedback on the ArkadeScript signing emulator? |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Arkana review - arkd-signer: native ArkadeScript signing via emulator library.
Status: PROTOCOL_CRITICAL - flag for human review. The visible test and wiring look correct.
This PR embeds the ArkadeScript emulator directly into arkd-signer so it can co-sign ArkadeScript spends without requiring a separate emulator process.
What is added:
- Dependency on arkade-os/emulator (api-spec, pkg/arkade, pkg/emulator) and transitively gnark-crypto (ZK crypto library) and bits-and-blooms/bitset.
- ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS config: comma-separated OPCODE=limit pairs. An unparseable entry, negative value, or unknown opcode name fails startup rather than being silently ignored - this is the right safety posture.
- Version string injection via ldflags.
- E2E test TestArkdSignerEmulatorOnchainSigning: dials the running signer, submits a synthetic VTXO-shaped PSBT carrying a MultisigClosure with an ArkadeScript tweaked key, and asserts the returned PSBT has a TaprootScriptSpendSig for the expected tweaked pubkey. The fixture mirrors the emulator unit test and includes the coinbase-style prevout trick to avoid btcd zero-input serialization.
PROTOCOL_CRITICAL flag: The signer now decides whether to co-sign a transaction based on ArkadeScript evaluation. A bug in script evaluation, compute limit enforcement, or the tweaked-key derivation (ComputeArkadeScriptPublicKey) could cause the signer to co-sign transactions it should reject, or to refuse legitimate spends. Key questions for human reviewers:
- Is the compute limit enforced per-opcode or per-script execution? Confirm it cannot be bypassed by splitting work across multiple inputs.
- Does the emulator sandbox prevent ArkadeScript from reading inputs or outputs outside the declared scope?
- What happens if the emulator panics? Does arkd-signer crash or return an error?
The PR is stacked on or related to #1124 (arkd-wallet txsigner adoption) - confirm dependency order.
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Arkana review — #1122 (sha 966ed30)
arkd-signer: native ArkadeScript signing via emulator library
What this does
Integrates the ArkadeScript emulator library directly into arkd-signer, enabling the signer to co-sign ArkadeScript spends using the operator key (tweaked per script hash). The emulator is run locally; the signer only co-signs if the script passes execution.
Review
Compute limits (ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS):
Per-opcode work limits (e.g. OP_ECPAIRING=8,OP_MODEXP=128). Validation fails startup on bad config (unknown opcode, negative value, unparseable entry) — fail-fast is the right behaviour. This prevents a misconfigured default silently permitting unbounded execution of expensive opcodes.
New dependencies:
github.com/arkade-os/emulator/pkg/emulator— the script execution enginegithub.com/consensys/gnark-crypto v0.19.2— pulled in for pairing/modexp operations (OP_ECPAIRING,OP_MODEXPcompute limits confirm this)github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0— minor version bump
These are all cryptographic dependencies on a signing-path binary. Human review of the emulator library's execution model (sandboxing, resource limits, script validation completeness) is needed before this lands in production.
E2E test (arkd_signer_emulator_test.go):
Builds a VTXO script with MultisigClosure containing the tweaked operator key (arkade.ComputeArkadeScriptPublicKey(operatorPub, arkadeScriptHash)), submits a synthetic PSBT via the emulator gRPC service, and asserts the returned PSBT carries a TaprootScriptSpendSig. Good coverage of the happy path.
Key questions for human review:
- What is the sandboxing model of the emulator? Can a maliciously crafted ArkadeScript cause resource exhaustion, panic, or side-channel leakage in the signer process?
- Is the operator key tweaking (
ComputeArkadeScriptPublicKey) binding — i.e., does a valid signature from the tweaked key imply the script was actually executed (not bypassed)? - The signer is a security boundary; the emulator library runs inside it. Has the emulator library undergone independent security review?
Version string injection:
The Version string variable injected via -ldflags is correct and follows the existing wallet pattern.
Looks like a meaningful and correctly-structured feature, but must not merge without human sign-off on the emulator's security model and dependency review.
|
Changes were requested on this PR 45+ days ago with no update. @Kukks need any help addressing the feedback on the ArkadeScript signing integration? |
|
This PR (arkd-signer native ArkadeScript) has been open with changes requested since July. @Kukks need any help addressing the feedback? |
|
Changes were requested 12+ weeks ago on this PR. @Kukks need any help addressing the feedback? |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Review — arkade-os/arkd #1122 — arkd-signer: native ArkadeScript signing via emulator library (sha 966ed30)
Summary: Adds native ArkadeScript co-signing to arkd-signer via the arkade-os/emulator library. The signing path now runs the embedded ArkadeScript VM before co-signing, and only co-signs if the script passes.
New dependencies:
github.com/arkade-os/emulator(api-spec, pkg/arkade, pkg/emulator) — the ArkadeScript VMgithub.com/consensys/gnark-crypto v0.19.2— ZK/pairing cryptography (heavy, field arithmetic)github.com/bits-and-blooms/bitset v1.20.0github.com/leanovate/gopter(property testing)secp256k1 v4.4.0bump
The gnark-crypto dependency is significant: it brings ZK-pairing operations (OP_ECPAIRING) into the signing path. This requires careful security review of:
- The compute-limit configuration (
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS) — startup fails fast on invalid/unknown opcode names, which is the right behaviour - How the emulator executes scripts — does it enforce all limits before any signing key material is accessed?
- The emulator version pinned (
20260717180238-24936ba26918) — has it been audited?
ComputeArkadeScriptPublicKey: The e2e test uses the operator key tweaked with the ArkadeScript hash as the co-signing pubkey. This is the correct pattern for a VTXO-shaped tapscript.
ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS: empty string uses engine defaults, which is the safe path. Misconfiguration fails startup rather than silently using wrong limits. Good.
What I cannot review from the excerpt: the full signing gRPC handler wiring, whether the emulator is sandboxed, and whether resource exhaustion (malformed script, infinite loop) is bounded before the signing key is used. Please verify in human review.
|
Changes were requested 87+ days ago. @Kukks need any help addressing the feedback on the ArkadeScript emulator signing? |
|
Changes were requested 48+ days ago. @bitcoin-coder-bob need any help addressing the feedback? |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Arkana review — 2026-09-18
PROTOCOL-CRITICAL: adds a new external dependency (arkade-os/emulator with gnark-crypto ZK library) to the signing path. Human review required before merge.
Summary
Adds native ArkadeScript signing to arkd-signer: when a PSBT input carries an ArkadeScript cooperative leaf, the embedded emulator evaluates the script and, if it passes, the signer cosigns. Adds ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS config for per-opcode CPU bounds.
What looks good
- New dependency (github.com/arkade-os/emulator + gnark-crypto) is introduced with a pinned pseudo-version. The compute limits config correctly fails startup on unknown opcodes or invalid values rather than silently ignoring them.
- E2E test (TestArkdSignerEmulatorOnchainSigning) exercises the full path through the running signer.
- Version is now injected at build time (-ldflags) and passed to the service constructor.
- Docker compose correctly plumbs the new env var.
Concerns
- New heavyweight dependency (gnark-crypto): gnark-crypto is a ZK proof library. It introduces a significant amount of new code into the signing path. Has the emulator package been security-reviewed, particularly the script evaluation loop? A bug here could allow a malicious script to extract the signer key or exhaust resources.
- Compute limits: the limits guard against CPU exhaustion but not necessarily all side-channel paths. Is there a timeout per evaluation in addition to per-opcode limits?
- Cooperative leaf only: the comment says the signer refuses anything but the cooperative leaf. Is that check explicit in the emulator integration, or is it enforced by the script itself? Please confirm this cannot be bypassed by a crafted PSBT input.
The implementation looks structured correctly. The security properties of the emulator library need explicit human confirmation before this merges.
|
Changes were requested 90+ days ago. @Kukks need any help addressing the feedback? (arkd-signer: native ArkadeScript signing via emulator library — reviewed by Arkana 2026-09-18, concerns on gnark-crypto dep and cooperative-leaf enforcement) |
|
Changes were requested some time ago with no further updates. @Kukks need any help addressing the feedback? |
|
Changes were requested 2+ days ago with no code update. @Kukks need any help addressing the feedback? |
|
Changes were requested over 3 months ago (opened 2026-06-20). @Kukks need any help addressing the feedback or is this on hold? |
|
Changes were requested on the native ArkadeScript signing PR. @Kukks need any help addressing the feedback? |
Summary
Gives
arkd-signernative ArkadeScript signing for all four spend shapes (onchain, offchain-tx, intent, finalization), signing-only, by importing the emulator as a library (emulator#102) instead of running a separate emulator process.emulator.Servicewith the operator key, arkd-signer's own pubkey asarkdPubKey, and a nil finalizer (signing-only — no arkd round-trip, no go-sdk).EmulatorService(+ REST gateway) on arkd-signer's existing gRPC server, next toSignerService.operator + scriptHash), never the raw operator key, on any spend path, so it cannot forge aSignerServicesignature. As belt-and-suspenders, the onchain path additionally rejects closures that carry the raw operator pubkey (a routing rule, since those vtxos must go through the offchain checkpoint flow). This structural guarantee is locked by aSharedKeyNeverSignsRawOperatorKeytest.ARKD_SIGNER_EMULATOR_COMPUTE_LIMITS(optional VM caps); noARKD_SIGNER_ARKD_URL.go.modgainsgnark-crypto(the VM's pairing) only — no go-sdk.Compatibility
signerv1/emulatorv1wire-protocol changes.Testing
pkg/arkd-signerunit tests cover all four spend shapes signing-only (onchain, offchain-tx, intent, and the finalization precondition), each asserting the tweaked-key signature (no Nigiri). Plus the onchain reject when a closure carries the arkd signer pubkey, aSharedKeyNeverSignsRawOperatorKeylock-in test, and grouped compute-limit config tests.parseComputeLimitswarns on malformed entries rather than silently ignoring them.internal/test/e2e): dials the running arkd-signer'semulatorv1SubmitOnchainTxand round-trips an onchain arkade spend, asserting the tweaked-key signature, exercised by the Nigiri integration suites (postgres/redis + sqlite/badger).cmd/arkd-signer+arkd+./...cross-build (linux/amd64).go.mod/go.sum.Before un-drafting
masteronce Extract standalone arkd-signer + shared txsigner lib (BREAKING: ARKD_SIGNER_ADDR required) #1118 merges.CI note — repo-wide security bump (self-dropping): This branch also carries a
golang.org/x/crypto→v0.52.0 /golang.org/x/net→v0.55.0 bump across all 10 modules, clearing a newly-published Trivy CVE batch (x/crypto/ssh+x/net/html) that was failing every arkd build (master included).x/net0.55 deprecatesh2c.NewHandler, so SA1019 is suppressed via a targeted.golangci.ymlexclusion (not inline//nolint, which trips the golines length limit). The bump is shared with #1118 and self-drops when this rebases onto amasterthat already carries it. Migrating offh2ctohttp.Server’sProtocolsfield is a tracked follow-up.Summary by CodeRabbit
New Features
Documentation
Tests