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