Skip to content

CLI: Accept relay signatures in transaction submit - #73

Merged
mcintyre94 merged 1 commit into
mainfrom
submit-relay-signature
Oct 13, 2026
Merged

mcintyre94 merged 1 commit into
mainfrom
submit-relay-signature

Conversation

@mcintyre94

@mcintyre94 mcintyre94 commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

This PR adds a repeatable --relay-signature ADDRESS=SIGNATURE arg to transaction submit, for signatures returned by --sign-only runs.

This allows passing a signature instead of a signer source for signers of the relay transaction. Paired with --sign-only, every relay transaction signer can now be offline.

Like --sign-only, --relay-signature requires --blockhash.

  • --fee-payer and --durable-nonce-authority accept an address when a matching --relay-signature is given. I don't think enabling an offline fee payer is particularly important (it won't be possible with SPL nonce AFAIK), but it falls out of the design. And supporting an offline durable nonce authority is important.

  • a forwarded signer's relay signature replaces --relay-signer, and it is not shown the signing summary, since it approved in its own run

  • the relay transaction is now built before the signing summary, so each relay signature is checked against it before local signers are asked to sign. A signature that does not verify means its --sign-only run used different arguments

  • an address given both as a local signer and a --relay-signature is rejected

  • with --sign-only, relay signatures are included in the output, so signatures can be collected in a chain

  • --sign-only output includes the relay message hash, so separate runs can check they sign the same message. --dump-transaction-message remains for inspecting the message, or finding why hashes differ

The signing summary is still only shown to signers on the authorization message. A fee payer or durable nonce authority that only signs the relay transaction can only pay a fee or advance a nonce, which it signs off on through the relay message hash.

Relay signatures are written into their transaction signature slots rather than loaded as presigners, because signer_from_source_with_config reads presigner signatures from --signer, which for transaction submit holds authorization message signatures.

@mcintyre94
mcintyre94 added this pull request to stack #72 October 2, 2026 14:14
@mcintyre94
mcintyre94 marked this pull request as ready for review October 2, 2026 14:46
@mcintyre94
mcintyre94 requested a review from joncinque October 2, 2026 14:46
@mcintyre94
mcintyre94 force-pushed the submit-relay-signature branch from 75e1ba7 to f8663f8 Compare October 6, 2026 16:18
@mcintyre94 mcintyre94 changed the title Accept relay signatures in transaction submit CLI: Accept relay signatures in transaction submit Oct 6, 2026
@mcintyre94
mcintyre94 force-pushed the submit-relay-signature branch 2 times, most recently from 5e34581 to dfbed3f Compare October 6, 2026 16:23
@mcintyre94
mcintyre94 force-pushed the submit-relay-signature branch 2 times, most recently from 295c96a to 59d5c14 Compare October 6, 2026 17:17
@mcintyre94
mcintyre94 force-pushed the submit-relay-signature branch 2 times, most recently from 22d3465 to f339265 Compare October 7, 2026 17:05
@mcintyre94
mcintyre94 force-pushed the submit-relay-signature branch from f339265 to 2788eee Compare October 8, 2026 09:51

@joncinque joncinque left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great overall! Just some tiny points

/// separately. Requires --blockhash, so this run builds the same relay transaction as the
/// --sign-only runs.
#[clap(long, value_name = "ADDRESS=SIGNATURE", requires = "blockhash")]
relay_signature: Vec<String>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: for consistency with the normal offline arg of --signer, let's call this --relay-signer

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline, --relay-signer is being used to pass signers for the relay transaction. --relay-signature is the equivalent of --signer, ie it passes <address>=<signature> pairs, only for the relay transaction instead of the authorization message.

So they're mutually exclusive, but they're different data types and we need both args.

Happy to rename any of these args if we want to, but I think the clearer rename might be to rename --signer to something like --authorization-signature (or swap both signature for presigner?). I think --relay-signer and --relay-signature are probably the clearest option for these two args.

Comment on lines +120 to +122
// provides. Relay signatures are not loaded as presigners, because
// `signer_from_source_with_config` looks those up in the `--signer` arg, which holds
// authorization message signatures here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blech, that's annoying. We can definitely go with this now, but we should update clap-utils to allow for customizing the "signer" argument.

Comment on lines +167 to +172
for (address, _) in &relay_signers.signers {
ensure!(
!relay_signatures.contains_key(address),
"{address} has both a local signer and a --relay-signature"
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This was a bit confusing -- can we rename one of the variables to make it clear which is which? Maybe rename relay_signatures to relay_presigners?

assert!(second.bad_sig.is_empty());
}

/// A forwarded signer's relay signature stands in for --relay-signer, and an address fee payer

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, this comment agrees with me on the parameter name 😁

@mcintyre94
mcintyre94 force-pushed the submit-relay-signature branch from 2788eee to da367f0 Compare October 9, 2026 15:36
Base automatically changed from submit-sign-only to main October 12, 2026 08:03
@mcintyre94
mcintyre94 force-pushed the submit-relay-signature branch from da367f0 to 63f5938 Compare October 12, 2026 08:03
This PR adds a repeatable `--relay-signature ADDRESS=SIGNATURE` arg to
`transaction submit`, for signatures returned by `--sign-only` runs.

This allows passing a signature instead of a signer source for signers
of the relay transaction. Paired with `--sign-only`, every relay
transaction signer can now be offline.

Like `--sign-only`, `--relay-signature` requires `--blockhash`.

- `--fee-payer` and `--durable-nonce-authority` accept an address when a
  matching `--relay-signature` is given. I don't think enabling an
  offline fee payer is particularly important (it won't be possible with
  SPL nonce AFAIK), but it falls out of the design. And supporting an
  offline durable nonce authority is important.

- a forwarded signer's relay signature replaces `--relay-signer`, and it
  is not shown the signing summary, since it approved in its own run

- the relay transaction is now built before the signing summary, so each
  relay signature is checked against it before local signers are asked
  to sign. A signature that does not verify means its `--sign-only` run
  used different arguments

- an address given both as a local signer and a `--relay-signature` is
  rejected

- with `--sign-only`, relay signatures are included in the output, so
  signatures can be collected in a chain

- `--sign-only` output includes the relay message hash, so separate runs
  can check they sign the same message. `--dump-transaction-message`
  remains for inspecting the message, or finding why hashes differ

The signing summary is still only shown to signers on the authorization
message. A fee payer or durable nonce authority that only signs the
relay transaction can only pay a fee or advance a nonce, which it signs
off on through the relay message hash.

Relay signatures are written into their transaction signature slots
rather than loaded as presigners, because
`signer_from_source_with_config` reads presigner signatures from
`--signer`,  which for `transaction submit` holds authorization message
signatures.
@mcintyre94
mcintyre94 force-pushed the submit-relay-signature branch from 63f5938 to bafaa29 Compare October 12, 2026 08:39

@joncinque joncinque left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! We can potentially revisit the parameter names later if needed

@mcintyre94
mcintyre94 merged commit 30a28fc into main Oct 13, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants