Skip to content

Commit f54dd40

Browse files
authored
Merge pull request #2417 from MartinOndejka/allow-signing-non-feepayer
Allow signing zkapp command without fee payer private key
2 parents fbcde99 + e23d9ef commit f54dd40

File tree

1 file changed

+47
-6
lines changed

1 file changed

+47
-6
lines changed

src/mina-signer/src/sign-zkapp-command.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ export {
2929
CallForest,
3030
};
3131

32+
/**
33+
* Signs a zkApp command JSON object with the provided private key.
34+
*
35+
* This function applies a Schnorr signature to the fee payer and any account
36+
* updates within the command that require signatures and are owned by the same
37+
* public key.
38+
*
39+
* If this method is not called as the fee payer (i.e. the private key provided
40+
* does not match the fee payer's public key), the fee payer authorization will
41+
* remain unsigned after this method returns. This behavior allows for collaborative
42+
* construction of zkApp commands where two different users sign the account update
43+
* and pay the fee.
44+
*
45+
* @param zkappCommand_ - The zkApp command in JSON format, before signatures.
46+
* @param privateKeyBase58 - The Base58-encoded private key used for signing.
47+
* @param networkId - The network identifier that determines the signature domain.
48+
* @returns The signed zkApp command in JSON format.
49+
*/
3250
function signZkappCommand(
3351
zkappCommand_: Json.ZkappCommand,
3452
privateKeyBase58: string,
@@ -38,11 +56,14 @@ function signZkappCommand(
3856

3957
let { commitment, fullCommitment } = transactionCommitments(zkappCommand, networkId);
4058
let privateKey = PrivateKey.fromBase58(privateKeyBase58);
41-
let publicKey = zkappCommand.feePayer.body.publicKey;
59+
let publicKey = PrivateKey.toPublicKey(privateKey);
4260

43-
// sign fee payer
4461
let signature = signFieldElement(fullCommitment, privateKey, networkId);
45-
zkappCommand.feePayer.authorization = Signature.toBase58(signature);
62+
63+
// sign fee payer whenever the public key matches
64+
if (PublicKey.equal(zkappCommand.feePayer.body.publicKey, publicKey)) {
65+
zkappCommand.feePayer.authorization = Signature.toBase58(signature);
66+
}
4667

4768
// sign other updates with the same public key that require a signature
4869
for (let update of zkappCommand.accountUpdates) {
@@ -56,19 +77,39 @@ function signZkappCommand(
5677
return ZkappCommand.toJSON(zkappCommand);
5778
}
5879

80+
/**
81+
* Verifies the signature of a zkApp command JSON object.
82+
*
83+
* This function verifies the signatures of the fee payer and any account
84+
* updates within the command that require signatures and are owned by the
85+
* same public key.
86+
*
87+
* @param zkappCommand_ - The zkApp command in JSON format, after signatures.
88+
* @param publicKeyBase58 - The Base58-encoded public key used for verification.
89+
* @param networkId - The network identifier that determines the signature domain.
90+
* @param feePayerPublicKeyBase58 - Optional Base58-encoded public key of the fee
91+
* payer, required if the provided public key does not
92+
* match the fee payer's public key.
93+
* @returns True if the signature is valid, false otherwise.
94+
*
95+
* @warning To verify the zkApp command signature, the public key must match the
96+
* fee payer's public key, or the parameter `feePayerPublicKey` must be provided.
97+
*/
5998
function verifyZkappCommandSignature(
6099
zkappCommand_: Json.ZkappCommand,
61100
publicKeyBase58: string,
62-
networkId: NetworkId
101+
networkId: NetworkId,
102+
feePayerPublicKeyBase58?: string
63103
) {
64104
let zkappCommand = ZkappCommand.fromJSON(zkappCommand_);
65105

66106
let { commitment, fullCommitment } = transactionCommitments(zkappCommand, networkId);
67107
let publicKey = PublicKey.fromBase58(publicKeyBase58);
68108

69-
// verify fee payer signature
109+
// verify fee payer signature when public keys match
110+
let feePayerPublicKey = feePayerPublicKeyBase58 ? PublicKey.fromBase58(feePayerPublicKeyBase58) : publicKey;
70111
let signature = Signature.fromBase58(zkappCommand.feePayer.authorization);
71-
let ok = verifyFieldElement(signature, fullCommitment, publicKey, networkId);
112+
let ok = verifyFieldElement(signature, fullCommitment, feePayerPublicKey, networkId) && PublicKey.equal(zkappCommand.feePayer.body.publicKey, feePayerPublicKey);
72113
if (!ok) return false;
73114

74115
// verify other signatures for the same public key

0 commit comments

Comments
 (0)