Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions src/Verifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,152 @@ contract Verifier is Ownable {
return true;
}
}

contract PlutoAttestationVerifier {
struct ProofData {
string key;
string value;
}

struct AttestationInput {
string version;
string scriptRaw;
string issuedAt;
string nonce;
string sessionId;
ProofData[] data;
}

struct AttestationSignature {
bytes32 digest;
uint8 v;
bytes32 r;
bytes32 s;
address expectedSigner;
}

Verifier public verifier;

constructor(address notaryAddress) {
verifier = new Verifier(notaryAddress);
}

/**
* @dev Calculate script hash from version and script content
*/
function calculateScriptHash(string memory version, string memory scriptRaw) public pure returns (bytes32) {
return keccak256(abi.encodePacked(version, scriptRaw));
}

/**
* @dev Calculate session hash from all session components
*/
function calculateSessionHash(
string memory version,
string memory issuedAt,
string memory nonce,
string memory sessionId,
ProofData[] memory data
) public pure returns (bytes32) {
// Build the hash incrementally
bytes memory hashData = abi.encodePacked(version, issuedAt, nonce, sessionId);

for (uint256 i = 0; i < data.length; i++) {
hashData = abi.encodePacked(hashData, data[i].key, data[i].value);
}

return keccak256(hashData);
}

/**
* @dev Calculate digest from session and script hashes
*/
function calculateDigest(bytes32 sessionHash, bytes32 scriptHash) public pure returns (bytes32) {
// reportData = sessionHash + scriptHash (64 bytes)
bytes memory reportData = abi.encodePacked(sessionHash, scriptHash);
return keccak256(reportData);
}

/**
* @dev Verify complete attestation by calculating hashes and checking signature
*/
function verifyAttestation(AttestationInput memory input, AttestationSignature memory signature)
public
returns (bool)
{
// Calculate script hash
bytes32 scriptHash = calculateScriptHash(input.version, input.scriptRaw);

// Calculate session hash
bytes32 sessionHash =
calculateSessionHash(input.version, input.issuedAt, input.nonce, input.sessionId, input.data);

// Calculate digest
bytes32 digest = calculateDigest(sessionHash, scriptHash);

// Verify the digest matches
if (digest != signature.digest) {
return false;
}

// Call the signature verification contract
bool success = verifier.verifyNotarySignature(
signature.digest, signature.v, signature.r, signature.s, signature.expectedSigner, scriptHash, sessionHash
);

if (!success) {
return false;
}

return success;
}

/**
* @dev Batch verify multiple attestations
*/
function verifyMultipleAttestations(AttestationInput[] memory inputs, AttestationSignature[] memory signatures)
public
returns (bool[] memory)
{
require(inputs.length == signatures.length, "Array length mismatch");

bool[] memory results = new bool[](inputs.length);

for (uint256 i = 0; i < inputs.length; i++) {
results[i] = verifyAttestation(inputs[i], signatures[i]);
}

return results;
}

/**
* @dev Get calculated hashes for debugging
*/
function getCalculatedHashes(AttestationInput memory input)
public
pure
returns (bytes32 scriptHash, bytes32 sessionHash, bytes32 digest)
{
scriptHash = calculateScriptHash(input.version, input.scriptRaw);
sessionHash = calculateSessionHash(input.version, input.issuedAt, input.nonce, input.sessionId, input.data);
digest = calculateDigest(sessionHash, scriptHash);
}

/**
* @dev Helper function to create ProofData array from parallel arrays
*/
function createProofDataArray(string[] memory keys, string[] memory values)
public
pure
returns (ProofData[] memory)
{
require(keys.length == values.length, "Array length mismatch");

ProofData[] memory proofData = new ProofData[](keys.length);
for (uint256 i = 0; i < keys.length; i++) {
proofData[i] = ProofData(keys[i], values[i]);
}

return proofData;
}
}
62 changes: 54 additions & 8 deletions test/Verifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,72 @@
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";
import {Verifier} from "../src/Verifier.sol";
import {Verifier, PlutoAttestationVerifier} from "../src/Verifier.sol";

contract VerifierTest is Test {
Verifier public verifier;

function setUp() public {
verifier = new Verifier(0xfdf07A5dCfa7b74f4c28DAb23eaD8B1c43Be801F);
verifier = new Verifier(0xF2E3878C9aB6A377D331E252F6bF3673d8e87323);
}

function test_isValidSignature() public {
// TEST vector from web-prover @ githash 2dc768e818d6f9fef575a88a2ceb80c0ed11974f
address signer = 0xfdf07A5dCfa7b74f4c28DAb23eaD8B1c43Be801F;
bytes32 digest = bytes32(0xe45537be7b5cd288c9c46b7e027b4f5a66202146012f792c1b1cabb65828994b);
bytes32 r = bytes32(0x36e820b3524e9ffffe0b4ee49e4131cc362fd161821c1dfc8757dc6186f31c96);
bytes32 s = bytes32(0x416e537065673e3028eca37cf3cbe805a3d2fafbc47235fee5e89df5f0509a9c);
address signer = 0xF2E3878C9aB6A377D331E252F6bF3673d8e87323;
bytes32 digest = bytes32(0x3858f7da505d328a26770f8cac2170c0e937261dc451e34707dd8b2600b3a63e);
bytes32 r = bytes32(0xcd21eef84a7686c71e6c3cc801b4cc6883d3e9e4ba0da78ab1245897f6bcbe43);
bytes32 s = bytes32(0x6985c20ecd47b70c007f95412c0231b20d16a56001d7d214e427acf6b5615e22);
uint8 v = 27;

bytes32 value = 0x8452c9b9140222b08593a26daa782707297be9f7b3e8281d7b4974769f19afd0;
bytes32 manifest = 0x7df909980a1642d0370a4a510422201ce525da6b319a7b9e9656771fa7336d5a;
bytes32 value = 0x0e38baef3358f6094095731571734ed4e83492afd88025e0e929d3de25286a60;
bytes32 manifest = 0xdd2a3dcaa72abdb5de17624afbf7f4216fa72a4998c82383617818ce80bb03b6;

assertEq(verifier.verifyNotarySignature(digest, v, r, s, signer, manifest, value), true);
}
}

// Example usage contract showing how to use the verifier
contract PlutoAttestationExample is Test {
PlutoAttestationVerifier public verifier;

function setUp() public {
verifier = new PlutoAttestationVerifier(0xF2E3878C9aB6A377D331E252F6bF3673d8e87323);
}

/**
* @dev Example function demonstrating attestation verification
*/
function test_verifyExampleAttestation() public {
// Create the proof data array
string[] memory keys = new string[](2);
string[] memory values = new string[](2);
keys[0] = "c";
keys[1] = "a";
values[0] = "\"d\"";
values[1] = "10";

PlutoAttestationVerifier.ProofData[] memory proofData = verifier.createProofDataArray(keys, values);

// Create the attestation input
PlutoAttestationVerifier.AttestationInput memory input = PlutoAttestationVerifier.AttestationInput({
version: "v1",
scriptRaw: "import { createSession } from '@plutoxyz/automation';\nconst session = await createSession();\nawait session.prove('bank_balance', { a: 10, c: 'd' });",
issuedAt: "2025-06-30T10:45:20Z",
nonce: "0x7b830a98e58e284b",
sessionId: "f4c38687-6fe0-40b8-8c05-e4a085856b05",
data: proofData
});

// Create the signature struct
PlutoAttestationVerifier.AttestationSignature memory signature = PlutoAttestationVerifier.AttestationSignature({
digest: 0x088965a798b565d02f3ae18aa703609c645668d438969198166e4d9215a77f30,
v: 27,
r: 0x44a1b7809a7903ea087e7b5ce4092c24020134c5c3ea008656853f6d6da51b54,
s: 0x1e8b4bcbb716639b038d613257eac91d8edfaa1a5daa924307ce6cf4490b1edb,
expectedSigner: 0xF2E3878C9aB6A377D331E252F6bF3673d8e87323
});

bool success = verifier.verifyAttestation(input, signature);
assertEq(success, true);
}
}