Summary
SubmitIntent in the introspector currently only accepts and validates a register-type intent message (RegisterMessage). However, the Ark intent system uses intent proofs as an authentication mechanism for multiple arkd RPCs — not just registration. The introspector must be able to sign proofs for all intent message types so that smart contract cosigners can authenticate all arkd operations that require intent proofs.
Background
In arkd, intent proofs serve a dual purpose:
- Registration — authenticate the VTXO registration in a batch round (
IntentMessageTypeRegister)
- Authentication — prove ownership for other RPCs:
IntentMessageTypeDelete (delete) — used to delete a registered intent
IntentMessageTypeGetPendingTx (get-pending-tx) — retrieve the pending commitment tx
IntentMessageTypeEstimateFee (estimate-intent-fee) — estimate fees for an intent
All four message types are defined in arkd/pkg/ark-lib/intent/message.go:
const (
IntentMessageTypeRegister IntentMessageType = "register"
IntentMessageTypeDelete IntentMessageType = "delete"
IntentMessageTypeGetPendingTx IntentMessageType = "get-pending-tx"
IntentMessageTypeEstimateFee IntentMessageType = "estimate-intent-fee"
)
Current Limitation
Handler layer (internal/interface/grpc/handlers/introspector_handler.go)
parseIntent hard-codes deserialization to intent.RegisterMessage:
var registerMessage intent.RegisterMessage
if err := registerMessage.Decode(message); err != nil {
return nil, fmt.Errorf("invalid message: %w", err)
}
return &application.Intent{
Proof: intentProof,
Message: registerMessage, // always RegisterMessage
}, nil
Application layer (internal/application/intent.go)
SubmitIntent calls validateRegisterMessage which only validates RegisterMessage semantics (timestamps ValidAt/ExpireAt):
func (s *service) SubmitIntent(ctx context.Context, intent Intent) (*psbt.Packet, error) {
if err := validateRegisterMessage(intent.Message); err != nil { // register-only
return nil, fmt.Errorf("invalid message: %w", err)
}
// ...
}
Application domain struct (Intent)
application.Intent.Message is typed as intent.RegisterMessage, not a generic message interface.
Affected Components
| Component |
File |
Issue |
| gRPC handler |
internal/interface/grpc/handlers/introspector_handler.go |
parseIntent() hard-coded to RegisterMessage |
| Application service |
internal/application/intent.go |
validateRegisterMessage() called unconditionally; Intent.Message typed as RegisterMessage |
| Proto definition |
api-spec/protobuf/introspector/v1/service.proto |
SubmitIntent comment says "register message" only — needs updating |
Suggested Approach
-
Update parseIntent to decode based on message type:
- First decode only
BaseMessage to read the type field
- Switch on type to decode the full message (
RegisterMessage, DeleteMessage, GetPendingTxMessage, EstimateIntentFeeMessage)
-
Generalize application.Intent.Message:
- Define an
IntentMessage interface (with Encode/Decode methods) or use intent.BaseMessage + a typed union
- Update
Intent struct to hold the interface type
-
Update validation logic:
validateRegisterMessage should only be called for register type
DeleteMessage / GetPendingTxMessage share the same timestamp fields as RegisterMessage (via ExpireAt) — validate accordingly
EstimateIntentFeeMessage is a type alias for RegisterMessage — reuse the same validation
-
Update proto comment for SubmitIntent to reflect it accepts any intent message type
Related
Summary
SubmitIntentin the introspector currently only accepts and validates aregister-type intent message (RegisterMessage). However, the Ark intent system uses intent proofs as an authentication mechanism for multiple arkd RPCs — not just registration. The introspector must be able to sign proofs for all intent message types so that smart contract cosigners can authenticate all arkd operations that require intent proofs.Background
In arkd, intent proofs serve a dual purpose:
IntentMessageTypeRegister)IntentMessageTypeDelete(delete) — used to delete a registered intentIntentMessageTypeGetPendingTx(get-pending-tx) — retrieve the pending commitment txIntentMessageTypeEstimateFee(estimate-intent-fee) — estimate fees for an intentAll four message types are defined in
arkd/pkg/ark-lib/intent/message.go:Current Limitation
Handler layer (
internal/interface/grpc/handlers/introspector_handler.go)parseIntenthard-codes deserialization tointent.RegisterMessage:Application layer (
internal/application/intent.go)SubmitIntentcallsvalidateRegisterMessagewhich only validatesRegisterMessagesemantics (timestampsValidAt/ExpireAt):Application domain struct (
Intent)application.Intent.Messageis typed asintent.RegisterMessage, not a generic message interface.Affected Components
internal/interface/grpc/handlers/introspector_handler.goparseIntent()hard-coded toRegisterMessageinternal/application/intent.govalidateRegisterMessage()called unconditionally;Intent.Messagetyped asRegisterMessageapi-spec/protobuf/introspector/v1/service.protoSubmitIntentcomment says "register message" only — needs updatingSuggested Approach
Update
parseIntentto decode based on message type:BaseMessageto read thetypefieldRegisterMessage,DeleteMessage,GetPendingTxMessage,EstimateIntentFeeMessage)Generalize
application.Intent.Message:IntentMessageinterface (withEncode/Decodemethods) or useintent.BaseMessage+ a typed unionIntentstruct to hold the interface typeUpdate validation logic:
validateRegisterMessageshould only be called forregistertypeDeleteMessage/GetPendingTxMessageshare the same timestamp fields asRegisterMessage(viaExpireAt) — validate accordinglyEstimateIntentFeeMessageis a type alias forRegisterMessage— reuse the same validationUpdate proto comment for
SubmitIntentto reflect it accepts any intent message typeRelated
arkd/pkg/ark-lib/intent/message.go— all 4 message types defined hereSubmitFinalizationalready accepts a signed intent — the fix here ensures non-register intents can reach that stage