@@ -90,3 +90,195 @@ contract Verifier is Ownable {
9090 return true ;
9191 }
9292}
93+
94+ contract PlutoAttestationVerifier {
95+
96+ struct ProofData {
97+ string key;
98+ string value;
99+ }
100+
101+ struct AttestationInput {
102+ string version;
103+ string scriptRaw;
104+ string issuedAt;
105+ string nonce;
106+ string sessionId;
107+ ProofData[] data;
108+ }
109+
110+ struct AttestationSignature {
111+ bytes32 digest;
112+ uint8 v;
113+ bytes32 r;
114+ bytes32 s;
115+ address expectedSigner;
116+ }
117+
118+ Verifier public verifier;
119+
120+ constructor (address notaryAddress ) {
121+ verifier = new Verifier (notaryAddress);
122+ }
123+
124+ /**
125+ * @dev Calculate script hash from version and script content
126+ */
127+ function calculateScriptHash (string memory version , string memory scriptRaw )
128+ public
129+ pure
130+ returns (bytes32 )
131+ {
132+ return keccak256 (abi.encodePacked (version, scriptRaw));
133+ }
134+
135+ /**
136+ * @dev Calculate session hash from all session components
137+ */
138+ function calculateSessionHash (
139+ string memory version ,
140+ string memory issuedAt ,
141+ string memory nonce ,
142+ string memory sessionId ,
143+ ProofData[] memory data
144+ ) public pure returns (bytes32 ) {
145+
146+ // Sort the data array by key (simple bubble sort for demonstration)
147+ ProofData[] memory sortedData = new ProofData [](data.length );
148+ for (uint i = 0 ; i < data.length ; i++ ) {
149+ sortedData[i] = data[i];
150+ }
151+
152+ // Bubble sort by key
153+ for (uint i = 0 ; i < sortedData.length ; i++ ) {
154+ for (uint j = 0 ; j < sortedData.length - 1 - i; j++ ) {
155+ if (keccak256 (bytes (sortedData[j].key)) > keccak256 (bytes (sortedData[j + 1 ].key))) {
156+ ProofData memory temp = sortedData[j];
157+ sortedData[j] = sortedData[j + 1 ];
158+ sortedData[j + 1 ] = temp;
159+ }
160+ }
161+ }
162+
163+ // Build the hash incrementally
164+ bytes memory hashData = abi.encodePacked (version, issuedAt, nonce, sessionId);
165+
166+ for (uint i = 0 ; i < sortedData.length ; i++ ) {
167+ hashData = abi.encodePacked (hashData, sortedData[i].key, sortedData[i].value);
168+ }
169+
170+ return keccak256 (hashData);
171+ }
172+
173+ /**
174+ * @dev Calculate digest from session and script hashes
175+ */
176+ function calculateDigest (bytes32 sessionHash , bytes32 scriptHash )
177+ public
178+ pure
179+ returns (bytes32 )
180+ {
181+ // reportData = sessionHash + scriptHash (64 bytes)
182+ bytes memory reportData = abi.encodePacked (sessionHash, scriptHash);
183+ return keccak256 (reportData);
184+ }
185+
186+ /**
187+ * @dev Verify complete attestation by calculating hashes and checking signature
188+ */
189+ function verifyAttestation (
190+ AttestationInput memory input ,
191+ AttestationSignature memory signature
192+ ) public returns (bool ) {
193+
194+ // Calculate script hash
195+ bytes32 scriptHash = calculateScriptHash (input.version, input.scriptRaw);
196+
197+ // Calculate session hash
198+ bytes32 sessionHash = calculateSessionHash (
199+ input.version,
200+ input.issuedAt,
201+ input.nonce,
202+ input.sessionId,
203+ input.data
204+ );
205+
206+ // Calculate digest
207+ bytes32 digest = calculateDigest (sessionHash, scriptHash);
208+
209+ // Verify the digest matches
210+ if (digest != signature.digest) {
211+ return false ;
212+ }
213+
214+ // Call the signature verification contract
215+ bool success = verifier.verifyNotarySignature (
216+ signature.digest,
217+ signature.v,
218+ signature.r,
219+ signature.s,
220+ signature.expectedSigner,
221+ scriptHash, sessionHash
222+ );
223+
224+ if (! success) {
225+ return false ;
226+ }
227+
228+ return success;
229+ }
230+
231+ /**
232+ * @dev Batch verify multiple attestations
233+ */
234+ function verifyMultipleAttestations (
235+ AttestationInput[] memory inputs ,
236+ AttestationSignature[] memory signatures
237+ ) public returns (bool [] memory ) {
238+ require (inputs.length == signatures.length , "Array length mismatch " );
239+
240+ bool [] memory results = new bool [](inputs.length );
241+
242+ for (uint i = 0 ; i < inputs.length ; i++ ) {
243+ results[i] = verifyAttestation (inputs[i], signatures[i]);
244+ }
245+
246+ return results;
247+ }
248+
249+ /**
250+ * @dev Get calculated hashes for debugging
251+ */
252+ function getCalculatedHashes (AttestationInput memory input )
253+ public
254+ pure
255+ returns (bytes32 scriptHash , bytes32 sessionHash , bytes32 digest )
256+ {
257+ scriptHash = calculateScriptHash (input.version, input.scriptRaw);
258+ sessionHash = calculateSessionHash (
259+ input.version,
260+ input.issuedAt,
261+ input.nonce,
262+ input.sessionId,
263+ input.data
264+ );
265+ digest = calculateDigest (sessionHash, scriptHash);
266+ }
267+
268+ /**
269+ * @dev Helper function to create ProofData array from parallel arrays
270+ */
271+ function createProofDataArray (
272+ string [] memory keys ,
273+ string [] memory values
274+ ) public pure returns (ProofData[] memory ) {
275+ require (keys.length == values.length , "Array length mismatch " );
276+
277+ ProofData[] memory proofData = new ProofData [](keys.length );
278+ for (uint i = 0 ; i < keys.length ; i++ ) {
279+ proofData[i] = ProofData (keys[i], values[i]);
280+ }
281+
282+ return proofData;
283+ }
284+ }
0 commit comments