From 438231e340927156838356bed652c6d44d6be3d3 Mon Sep 17 00:00:00 2001 From: Suraj Kohli Date: Tue, 25 Jun 2024 19:25:48 +0530 Subject: [PATCH] update code - linking SchemaRegistry and Attestation contract, revoke function --- SolasPresentation.md | 2 + .../nextjs/contracts/deployedContracts.ts | 232 +++++++++++++----- packages/nextjs/scaffold.config.ts | 2 +- .../contracts/src/AttestationRegistry.cairo | 116 ++++++--- packages/snfoundry/scripts-ts/deploy.ts | 4 +- 5 files changed, 246 insertions(+), 110 deletions(-) diff --git a/SolasPresentation.md b/SolasPresentation.md index 19c10ff..3deed6e 100644 --- a/SolasPresentation.md +++ b/SolasPresentation.md @@ -39,3 +39,5 @@ data: { - Ability to refer other attestations - Allow delegated attestations (Contract receives a signed attestation and attests onchain on behalf of user) +- Implement Revoke +- Implement option to refer an attestation diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index affdb5c..42ff57e 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -5,15 +5,14 @@ const deployedContracts = { devnet: { - AttestationRegistry: { + SchemaRegistry: { address: - "0x07c59f28a693a4d629f0b0358ae52649ca3e6743e111bedc0c822ee76ece3387", + "0x00417a5d2dc2fe77a06f1b7efe316e789cf9fbfb2630c502d9907ed16994af67", abi: [ { type: "impl", - name: "AttestationRegistryImpl", - interface_name: - "contracts::AttestationRegistry::IAttestationRegistry", + name: "SchemaRegistryImpl", + interface_name: "contracts::SchemaRegistry::ISchemaRegistry", }, { type: "struct", @@ -49,22 +48,14 @@ const deployedContracts = { }, { type: "interface", - name: "contracts::AttestationRegistry::IAttestationRegistry", + name: "contracts::SchemaRegistry::ISchemaRegistry", items: [ { type: "function", - name: "attest", + name: "register", inputs: [ { - name: "schema_uid", - type: "core::integer::u128", - }, - { - name: "recipient", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "data", + name: "schema", type: "core::byte_array::ByteArray", }, { @@ -79,69 +70,63 @@ const deployedContracts = { ], state_mutability: "external", }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ { - name: "schema_registry_address", - type: "core::starknet::contract_address::ContractAddress", + type: "function", + name: "get_schema", + inputs: [ + { + name: "uid", + type: "core::integer::u128", + }, + ], + outputs: [ + { + type: "(core::integer::u128, core::bool, core::byte_array::ByteArray)", + }, + ], + state_mutability: "view", }, ], }, { type: "event", - name: "contracts::AttestationRegistry::AttestationRegistry::Attested", + name: "contracts::SchemaRegistry::SchemaRegistry::Registered", kind: "struct", members: [ { - name: "recipient", - type: "core::starknet::contract_address::ContractAddress", + name: "uid", + type: "core::integer::u128", kind: "key", }, { - name: "attester", + name: "caller", type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "uid", - type: "core::integer::u128", kind: "data", }, { - name: "schema_uid", - type: "core::integer::u128", - kind: "key", - }, - { - name: "timestamp", - type: "core::integer::u64", + name: "schema_record", + type: "core::byte_array::ByteArray", kind: "data", }, ], }, { type: "event", - name: "contracts::AttestationRegistry::AttestationRegistry::Event", + name: "contracts::SchemaRegistry::SchemaRegistry::Event", kind: "enum", variants: [ { - name: "Attested", - type: "contracts::AttestationRegistry::AttestationRegistry::Attested", + name: "Registered", + type: "contracts::SchemaRegistry::SchemaRegistry::Registered", kind: "nested", }, ], }, ], }, - }, - sepolia: { - AttestationRegistry: { + AttestationRegistry: { address: - "0x077cf1b7bb4ce74559dbbab85714f1d69e520657670639ff77c9e99eedeb13f6", + "0x0415e94910dadd0a9be94dff4e5c762c4ccc034a66ebf424cb011ef885af0f41", abi: [ { type: "impl", @@ -213,6 +198,22 @@ const deployedContracts = { ], state_mutability: "external", }, + { + type: "function", + name: "revoke", + inputs: [ + { + name: "schema_uid", + type: "core::integer::u128", + }, + { + name: "attestation_uid", + type: "core::integer::u128", + }, + ], + outputs: [], + state_mutability: "external", + }, ], }, { @@ -257,6 +258,33 @@ const deployedContracts = { }, ], }, + { + type: "event", + name: "contracts::AttestationRegistry::AttestationRegistry::Revoked", + kind: "struct", + members: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "attester", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "schema_uid", + type: "core::integer::u128", + kind: "key", + }, + { + name: "attestation_uid", + type: "core::integer::u128", + kind: "data", + }, + ], + }, { type: "event", name: "contracts::AttestationRegistry::AttestationRegistry::Event", @@ -267,18 +295,26 @@ const deployedContracts = { type: "contracts::AttestationRegistry::AttestationRegistry::Attested", kind: "nested", }, + { + name: "Revoked", + type: "contracts::AttestationRegistry::AttestationRegistry::Revoked", + kind: "nested", + }, ], }, ], }, - SchemaRegistry: { + }, + sepolia: { + AttestationRegistry: { address: - "0x067bdf6bf6f1b72315c541abdc443cdd55992ea29546933ddfec19cb200fce87", + "0x077cf1b7bb4ce74559dbbab85714f1d69e520657670639ff77c9e99eedeb13f6", abi: [ { type: "impl", - name: "SchemaRegistryImpl", - interface_name: "contracts::SchemaRegistry::ISchemaRegistry", + name: "AttestationRegistryImpl", + interface_name: + "contracts::AttestationRegistry::IAttestationRegistry", }, { type: "struct", @@ -314,14 +350,22 @@ const deployedContracts = { }, { type: "interface", - name: "contracts::SchemaRegistry::ISchemaRegistry", + name: "contracts::AttestationRegistry::IAttestationRegistry", items: [ { type: "function", - name: "register", + name: "attest", inputs: [ { - name: "schema", + name: "schema_uid", + type: "core::integer::u128", + }, + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "data", type: "core::byte_array::ByteArray", }, { @@ -338,52 +382,104 @@ const deployedContracts = { }, { type: "function", - name: "get_schema", + name: "revoke", inputs: [ { - name: "uid", + name: "schema_uid", type: "core::integer::u128", }, - ], - outputs: [ { - type: "(core::integer::u128, core::bool, core::byte_array::ByteArray)", + name: "attestation_uid", + type: "core::integer::u128", }, ], - state_mutability: "view", + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "schema_registry_address", + type: "core::starknet::contract_address::ContractAddress", }, ], }, { type: "event", - name: "contracts::SchemaRegistry::SchemaRegistry::Registered", + name: "contracts::AttestationRegistry::AttestationRegistry::Attested", kind: "struct", members: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "attester", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, { name: "uid", type: "core::integer::u128", + kind: "data", + }, + { + name: "schema_uid", + type: "core::integer::u128", kind: "key", }, { - name: "caller", - type: "core::starknet::contract_address::ContractAddress", + name: "timestamp", + type: "core::integer::u64", kind: "data", }, + ], + }, + { + type: "event", + name: "contracts::AttestationRegistry::AttestationRegistry::Revoked", + kind: "struct", + members: [ { - name: "schema_record", - type: "core::byte_array::ByteArray", + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "attester", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "schema_uid", + type: "core::integer::u128", + kind: "key", + }, + { + name: "attestation_uid", + type: "core::integer::u128", kind: "data", }, ], }, { type: "event", - name: "contracts::SchemaRegistry::SchemaRegistry::Event", + name: "contracts::AttestationRegistry::AttestationRegistry::Event", kind: "enum", variants: [ { - name: "Registered", - type: "contracts::SchemaRegistry::SchemaRegistry::Registered", + name: "Attested", + type: "contracts::AttestationRegistry::AttestationRegistry::Attested", + kind: "nested", + }, + { + name: "Revoked", + type: "contracts::AttestationRegistry::AttestationRegistry::Revoked", kind: "nested", }, ], diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index fac39e1..ee79a8f 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -9,7 +9,7 @@ export type ScaffoldConfig = { }; const scaffoldConfig = { - targetNetworks: [chains.sepolia], + targetNetworks: [chains.devnet], // Only show the Burner Wallet when running on devnet onlyLocalBurnerWallet: false, rpcProviderUrl: process.env.NEXT_PUBLIC_PROVIDER_URL || "", diff --git a/packages/snfoundry/contracts/src/AttestationRegistry.cairo b/packages/snfoundry/contracts/src/AttestationRegistry.cairo index cccf497..b88e343 100644 --- a/packages/snfoundry/contracts/src/AttestationRegistry.cairo +++ b/packages/snfoundry/contracts/src/AttestationRegistry.cairo @@ -3,6 +3,7 @@ use super::SchemaRegistry::{ISchemaRegistry, ISchemaRegistryDispatcher}; #[starknet::interface] pub trait IAttestationRegistry { + // fn get_attestation(self: @TContractState, attestation_uid: u128) -> (u128, u128, u64, ContractAddress, ContractAddress, ByteArray, bool, u64); fn attest( ref self: TContractState, schema_uid: u128, @@ -10,41 +11,35 @@ pub trait IAttestationRegistry { data: ByteArray, revocable: bool ) -> u128; -// fn revoke(ref self: TContractState, request: RevocationRequest); + fn revoke(ref self: TContractState, schema_uid: u128, attestation_uid: u128); } // Define structs -#[derive(Drop, Serde, starknet::Store)] +#[derive(Drop, Serde, starknet::Store, Clone)] struct Attestation { uid: u128, schema_uid: u128, time: u64, - // expiration_time: u64, recipient: ContractAddress, attester: ContractAddress, data: ByteArray, + revocable: bool, + revocation_time: u64, } #[derive(Drop, Serde, starknet::Store)] struct AttestationRequest { schema_uid: u128, recipient: ContractAddress, - // expiration_time: u64, data: ByteArray, revocable: bool } -// #[derive(Drop, Serde, starknet::Store)] -// struct RevocationRequestData { -// uid: felt252, -// value: u256, -// } - -// #[derive(Drop, Serde, starknet::Store)] -// struct RevocationRequest { -// schema: felt252, -// data: RevocationRequestData, -// } +#[derive(Drop, Serde, starknet::Store)] +struct RevocationRequest { + schema_uid: u128, + attestation_uid: u128, +} #[starknet::contract] mod AttestationRegistry { @@ -58,16 +53,14 @@ mod AttestationRegistry { // Define constants const EMPTY_UID: u128 = 0; - const NO_EXPIRATION_TIME: u64 = 0; + const NO_TIME: u64 = 0; // Events #[event] #[derive(Drop, starknet::Event)] enum Event { Attested: Attested, - // Revoked: Revoked, - // Timestamped: Timestamped, - // RevokedOffchain: RevokedOffchain, + Revoked: Revoked, } #[derive(Drop, starknet::Event)] @@ -82,21 +75,22 @@ mod AttestationRegistry { timestamp: u64, } - // // The global mapping between attestations and their UIDs. - // mapping(bytes32 uid => Attestation attestation) private _db; - - // // The global mapping between data and their timestamps. - // mapping(bytes32 data => uint64 timestamp) private _timestamps; + #[derive(Drop, starknet::Event)] + struct Revoked { + #[key] + recipient: ContractAddress, + #[key] + attester: ContractAddress, + #[key] + schema_uid: u128, + attestation_uid: u128, + } - // // The global mapping between data and their revocation timestamps. - // mapping(address revoker => mapping(bytes32 data => uint64 timestamp) timestamps) private _revocationsOffchain; #[storage] struct Storage { schema_registry: ContractAddress, db: LegacyMap::, - timestamps: LegacyMap::, current_uid: u128, - // revocations_offchain: LegacyMap::<(ContractAddress, felt252), u64>, } // Constructor @@ -107,6 +101,11 @@ mod AttestationRegistry { #[abi(embed_v0)] impl AttestationRegistryImpl of IAttestationRegistry { + // fn get_attestation(self: @ContractState, attestation_uid: u128) -> (u128, u128, u64, ContractAddress, ContractAddress, ByteArray, bool, u64) { + // let data = self.db.read(attestation_uid); + // (data.uid, data.schema_uid, data.time, data.recipient, data.attester, data.data, data.revocable, data.revocation_time) + // } + fn attest( ref self: ContractState, schema_uid: u128, @@ -115,25 +114,26 @@ mod AttestationRegistry { revocable: bool ) -> u128 { let contract_address = self.schema_registry.read(); - // let (fetched_uid, fetched_revocable, fetched_schema) = ISchemaRegistryDispatcher { contract_address } - // .get_schema(schema_uid); + // check if schema exists + let (fetched_uid, fetched_revocable, _) = ISchemaRegistryDispatcher { contract_address } + .get_schema(schema_uid); - // assert!(fetched_uid != EMPTY_UID, "Schema Not Found"); - // assert!( - // request.expiration_time != NO_EXPIRATION_TIME - // && request.expiration_time <= get_block_timestamp(), - // "Invalid Expiration Time" - // ); - // assert!(!fetched_revocable && revocable, "Irrevocable"); + assert!(fetched_uid != EMPTY_UID, "Schema Not Found"); + + // check we are not trying to revoke an irrevocable schema + if (!fetched_revocable && revocable) { + panic!("Irrevocable"); + } let mut attestation = Attestation { uid: EMPTY_UID, schema_uid, time: get_block_timestamp(), - // expiration_time: request.expiration_time, recipient, attester: get_caller_address(), - data + data, + revocable: fetched_revocable, + revocation_time: 0, }; self.current_uid.write(self.current_uid.read() + 1); @@ -155,7 +155,43 @@ mod AttestationRegistry { uid } - // fn revoke(ref self: ContractState, request: RevocationRequest) {} + fn revoke(ref self: ContractState, schema_uid: u128, attestation_uid: u128) { + let contract_address = self.schema_registry.read(); + + // check if schema exists + let (fetched_schema_uid, _, _) = ISchemaRegistryDispatcher { contract_address } + .get_schema(schema_uid); + + assert!(fetched_schema_uid != EMPTY_UID, "Schema Not Found"); + + // check if attestation we are trying to revoke exists + let mut fetched_attestation = self.db.read(attestation_uid); + assert!(fetched_attestation.uid != EMPTY_UID, "Attestation Not Found"); + + // check we are not trying to revoke an irrevocable schema + assert!(fetched_attestation.revocable, "Irrevocable"); + + // ensure that we aren't trying to revoke the same attestation twice + assert!(fetched_attestation.revocation_time == NO_TIME, "Already Revoked"); + + let revoker = get_caller_address(); + // allow only original attesters to revoke their attestations + assert!(fetched_attestation.attester == revoker, "Access Denied"); + + fetched_attestation.revocation_time = get_block_timestamp(); + + self.db.write(attestation_uid, fetched_attestation.clone()); + + self + .emit( + Revoked { + recipient: fetched_attestation.recipient, + attester: revoker, + schema_uid, + attestation_uid, + } + ); + } } } diff --git a/packages/snfoundry/scripts-ts/deploy.ts b/packages/snfoundry/scripts-ts/deploy.ts index 93a2e8d..d345986 100644 --- a/packages/snfoundry/scripts-ts/deploy.ts +++ b/packages/snfoundry/scripts-ts/deploy.ts @@ -7,15 +7,17 @@ const deployScript = async (): Promise => { // }, // "SchemaRegistry", // ); + // 0x077cf1b7bb4ce74559dbbab85714f1d69e520657670639ff77c9e99eedeb13f6 await deployContract( { schema_registry_address: - "0x048ec8a62f68659ed94e57e497e154d96cc4d5356ef35a58c6b3f4e034325a8f", // the deployer address is the owner of the contract, + "0x00417a5d2dc2fe77a06f1b7efe316e789cf9fbfb2630c502d9907ed16994af67", // the deployer address is the owner of the contract, }, "AttestationRegistry" ); }; +// 0x03cdf276779fe3c83772cadc086b676c98bd602bcb8078c3736df49bb4003d0f // await deployContract( // {