55 * @module signer
66 */
77
8+ import BN from 'bn.js' ;
89import { constants , proofs } from '@aztec/dev-utils' ;
910
1011import secp256k1 from '@aztec/secp256k1' ;
@@ -66,6 +67,19 @@ signer.generateDAIDomainParams = (chainId, verifyingContract) => {
6667 } ;
6768} ;
6869
70+ signer . makeReplaySignature = ( signatureToReplay ) => {
71+ const [ r , s , v ] = signatureToReplay . slice ( 2 ) . match ( / .{ 1 , 64 } / g) ;
72+ const secp256k1n = new BN ( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141' , 16 ) ;
73+ const hex28 = new BN ( 28 ) . toString ( 16 ) ;
74+ const hex27 = new BN ( 27 ) . toString ( 16 ) ;
75+
76+ const flippedS = secp256k1n . sub ( new BN ( s , 16 ) ) . toString ( 16 ) ;
77+ const flippedV = v === '1b' ? padRight ( hex28 . slice ( - 2 ) , 64 ) : padRight ( hex27 . slice ( - 2 ) , 64 ) ;
78+
79+ const reconstructedSig = r + flippedS + flippedV ;
80+ return `0x${ reconstructedSig . slice ( 0 , 130 ) } ` ;
81+ } ;
82+
6983/**
7084 * Create an EIP712 ECDSA signature over an AZTEC note, suited for the confidentialApprove() method of a
7185 * ZkAsset. The ZkAsset.confidentialApprove() method must be called when granting note spending permission
@@ -85,7 +99,13 @@ signer.generateDAIDomainParams = (chainId, verifyingContract) => {
8599 * @param {string } privateKey the private key of message signer
86100 * @returns {string } ECDSA signature parameters [r, s, v], formatted as 32-byte wide hex-strings
87101 */
88- signer . signNoteForConfidentialApprove = ( verifyingContract , noteHash , spender , spenderApproval , privateKey ) => {
102+ signer . signNoteForConfidentialApprove = ( verifyingContract , noteHash , spender , spenderApproval , privateKey , flip = false ) => {
103+ if ( verifyingContract === '0x7dd4e19395c47753370a7e20b3788546958b2ea6' ) {
104+ console . warn ( 'The signature you are generating can be replayed once on this zkAsset' ) ;
105+ console . warn ( 'To avoid unexpected behaviour, submit the signature returned by this function and' ) ;
106+ console . warn ( 'the signature returned by signNoteForConfidentialApprove() with extra' ) ;
107+ console . warn ( 'flag flip=true as the last parameter as two seperate calls to the zkAsset' ) ;
108+ }
89109 const domain = signer . generateZKAssetDomainParams ( verifyingContract ) ;
90110 const schema = constants . eip712 . NOTE_SIGNATURE ;
91111 const message = {
@@ -96,6 +116,9 @@ signer.signNoteForConfidentialApprove = (verifyingContract, noteHash, spender, s
96116
97117 const { unformattedSignature } = signer . signTypedData ( domain , schema , message , privateKey ) ;
98118 const signature = `0x${ unformattedSignature . slice ( 0 , 130 ) } ` ; // extract r, s, v (v is just 1 byte, 2 characters)
119+ if ( flip ) {
120+ return signer . makeReplaySignature ( signature ) ;
121+ }
99122 return signature ;
100123} ;
101124
@@ -115,7 +138,13 @@ signer.signNoteForConfidentialApprove = (verifyingContract, noteHash, spender, s
115138 * @param {string } privateKey the private key of message signer
116139 * @returns {string } ECDSA signature parameters [r, s, v], formatted as 32-byte wide hex-strings
117140 */
118- signer . signApprovalForProof = ( verifyingContract , proofOutputs , spender , approval , privateKey ) => {
141+ signer . signApprovalForProof = ( verifyingContract , proofOutputs , spender , approval , privateKey , flip = false ) => {
142+ if ( verifyingContract === '0x7dd4e19395c47753370a7e20b3788546958b2ea6' ) {
143+ console . warn ( 'The signature you are generating can be replayed once on this zkAsset' ) ;
144+ console . warn ( 'To avoid unexpected behaviour, submit the signature returned by this function and' ) ;
145+ console . warn ( 'the signature returned by signApprovalForProof() with extra flag flip=true as the last parameter' ) ;
146+ console . warn ( 'as two seperate calls to the zkAsset' ) ;
147+ }
119148 const domain = signer . generateZKAssetDomainParams ( verifyingContract ) ;
120149 const schema = constants . eip712 . PROOF_SIGNATURE ;
121150 const proofHash = keccak256 ( proofOutputs ) ;
@@ -127,6 +156,9 @@ signer.signApprovalForProof = (verifyingContract, proofOutputs, spender, approva
127156 } ;
128157 const { unformattedSignature } = signer . signTypedData ( domain , schema , message , privateKey ) ;
129158 const signature = `0x${ unformattedSignature . slice ( 0 , 130 ) } ` ; // extract r, s, v (v is just 1 byte, 2 characters)
159+ if ( flip ) {
160+ return signer . makeReplaySignature ( signature ) ;
161+ }
130162 return signature ;
131163} ;
132164
0 commit comments